1 : <?php
2 : /*--------------------------------------------------------------------------+
3 : This file is part of eStudy.
4 : common/classes/class.geshi.inc.php
5 : - Modulgruppe: Framework
6 : - Beschreibung: Syntax-Highlighter.
7 : - Version: 0.2, 11/09/06
8 : - Autor(en): Nigel McNie <nigel@geshi.org>
9 : Clemens Weiß <clemens.weiss@mni.fh-giessen.de>
10 : +---------------------------------------------------------------------------+
11 : This program is free software; you can redistribute it and/or
12 : modify it under the terms of the GNU General Public License
13 : as published by the Free Software Foundation; either version 2
14 : of the License, or any later version.
15 : +---------------------------------------------------------------------------+
16 : This program is distributed in the hope that it will be useful,
17 : but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : GNU General Public License for more details.
20 : You should have received a copy of the GNU General Public License
21 : along with this program; if not, write to the Free Software
22 : Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 : +--------------------------------------------------------------------------*/
24 : /**
25 : * GeSHi - Generic Syntax Highlighter
26 : *
27 : * The GeSHi class for Generic Syntax Highlighting. Please refer to the
28 : * documentation at http://qbnz.com/highlighter/documentation.php for more
29 : * information about how to use this class.
30 : *
31 : * For changes, release notes, TODOs etc, see the relevant files in the docs/
32 : * directory.
33 : *
34 : * This file is part of GeSHi.
35 : *
36 : * GeSHi is free software; you can redistribute it and/or modify
37 : * it under the terms of the GNU General Public License as published by
38 : * the Free Software Foundation; either version 2 of the License, or
39 : * (at your option) any later version.
40 : *
41 : * GeSHi is distributed in the hope that it will be useful,
42 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
43 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
44 : * GNU General Public License for more details.
45 : *
46 : * You should have received a copy of the GNU General Public License
47 : * along with GeSHi; if not, write to the Free Software
48 : * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
49 : *
50 : * @package core
51 : * @author Nigel McNie <nigel@geshi.org>
52 : * @copyright Copyright © 2004, 2005, Nigel McNie
53 : * @license http://gnu.org/copyleft/gpl.html GNU GPL
54 : * @version $Id: class.geshi.inc.php,v 1.4.20.4 2008/01/07 14:36:21 commana Exp $
55 : *
56 : */
57 : //
58 : // GeSHi Constants
59 : // You should use these constant names in your programs instead of
60 : // their values - you never know when a value may change in a future
61 : // version
62 : //
63 :
64 : /** The version of this GeSHi file */
65 : define('GESHI_VERSION', '1.0.7.13');
66 : /** Set the correct directory separator */
67 : define('GESHI_DIR_SEPARATOR', ('WIN' != substr(PHP_OS, 0, 3)) ? '/' : '\\');
68 : // Define the root directory for the GeSHi code tree
69 : if (!defined('GESHI_ROOT')) {
70 : /** The root directory for GeSHi */
71 : define('GESHI_ROOT', dirname(__FILE__) .GESHI_DIR_SEPARATOR);
72 : }
73 : /** The language file directory for GeSHi
74 : @access private
75 : */
76 : define('GESHI_LANG_ROOT', GESHI_ROOT.'geshi'.GESHI_DIR_SEPARATOR);
77 : // Line numbers - use with enable_line_numbers()
78 :
79 : /** Use no line numbers when building the result */
80 : define('GESHI_NO_LINE_NUMBERS', 0);
81 : /** Use normal line numbers when building the result */
82 : define('GESHI_NORMAL_LINE_NUMBERS', 1);
83 : /** Use fancy line numbers when building the result */
84 : define('GESHI_FANCY_LINE_NUMBERS', 2);
85 : // Container HTML type
86 :
87 : /** Use nothing to surround the source */
88 : define('GESHI_HEADER_NONE', 0);
89 : /** Use a "div" to surround the source */
90 : define('GESHI_HEADER_DIV', 1);
91 : /** Use a "pre" to surround the source */
92 : define('GESHI_HEADER_PRE', 2);
93 : // Capatalisation constants
94 :
95 : /** Lowercase keywords found */
96 : define('GESHI_CAPS_NO_CHANGE', 0);
97 : /** Uppercase keywords found */
98 : define('GESHI_CAPS_UPPER', 1);
99 : /** Leave keywords found as the case that they are */
100 : define('GESHI_CAPS_LOWER', 2);
101 : // Link style constants
102 :
103 : /** Links in the source in the :link state */
104 : define('GESHI_LINK', 0);
105 : /** Links in the source in the :hover state */
106 : define('GESHI_HOVER', 1);
107 : /** Links in the source in the :active state */
108 : define('GESHI_ACTIVE', 2);
109 : /** Links in the source in the :visited state */
110 : define('GESHI_VISITED', 3);
111 : // Important string starter/finisher
112 : // Note that if you change these, they should be as-is: i.e., don't
113 : // write them as if they had been run through htmlentities()
114 :
115 : /** The starter for important parts of the source */
116 : define('GESHI_START_IMPORTANT', '<BEGIN GeSHi>');
117 : /** The ender for important parts of the source */
118 : define('GESHI_END_IMPORTANT', '<END GeSHi>');
119 : /**#@+
120 : * @access private
121 : */
122 : // When strict mode applies for a language
123 :
124 : /** Strict mode never applies (this is the most common) */
125 : define('GESHI_NEVER', 0);
126 : /** Strict mode *might* apply, and can be enabled or
127 : disabled by {@link GeSHi::enable_strict_mode()}
128 : */
129 : define('GESHI_MAYBE', 1);
130 : /** Strict mode always applies */
131 : define('GESHI_ALWAYS', 2);
132 : // Advanced regexp handling constants, used in language files
133 :
134 : /** The key of the regex array defining what to search for */
135 : define('GESHI_SEARCH', 0);
136 : /** The key of the regex array defining what bracket group in a
137 : matched search to use as a replacement
138 : */
139 : define('GESHI_REPLACE', 1);
140 : /** The key of the regex array defining any modifiers to the regular expression */
141 : define('GESHI_MODIFIERS', 2);
142 : /** The key of the regex array defining what bracket group in a
143 : matched search to put before the replacement
144 : */
145 : define('GESHI_BEFORE', 3);
146 : /** The key of the regex array defining what bracket group in a
147 : matched search to put after the replacement
148 : */
149 : define('GESHI_AFTER', 4);
150 : /** The key of the regex array defining a custom keyword to use
151 : for this regexp's html tag class
152 : */
153 : define('GESHI_CLASS', 5);
154 : /** Used in language files to mark comments */
155 : define('GESHI_COMMENTS', 0);
156 : // Error detection - use these to analyse faults
157 :
158 : /** No sourcecode to highlight was specified
159 : * @deprecated
160 : */
161 : define('GESHI_ERROR_NO_INPUT', 1);
162 : /** The language specified does not exist */
163 : define('GESHI_ERROR_NO_SUCH_LANG', 2);
164 : /** GeSHi could not open a file for reading (generally a language file) */
165 : define('GESHI_ERROR_FILE_NOT_READABLE', 3);
166 : /** The header type passed to {@link GeSHi::set_header_type()} was invalid */
167 : define('GESHI_ERROR_INVALID_HEADER_TYPE', 4);
168 : /** The line number type passed to {@link GeSHi::enable_line_numbers()} was invalid */
169 : define('GESHI_ERROR_INVALID_LINE_NUMBER_TYPE', 5);
170 : /**#@-*/
171 : /**
172 : * The GeSHi Class.
173 : *
174 : * Please refer to the documentation for GeSHi 1.0.X that is available
175 : * at http://qbnz.com/highlighter/documentation.php for more information
176 : * about how to use this class.
177 : *
178 : * @package core
179 : * @author Nigel McNie <nigel@geshi.org>
180 : * @copyright Copyright © 2004, 2005 Nigel McNie
181 : */
182 : class GeSHi {
183 : /**#@+
184 : * @access private
185 : */
186 : /**
187 : * The source code to highlight
188 : * @var string
189 : */
190 : var $source = '';
191 : /**
192 : * The language to use when highlighting
193 : * @var string
194 : */
195 : var $language = '';
196 : /**
197 : * The data for the language used
198 : * @var array
199 : */
200 : var $language_data = array();
201 : /**
202 : * The path to the language files
203 : * @var string
204 : */
205 : var $language_path = GESHI_LANG_ROOT;
206 : /**
207 : * The error message associated with an error
208 : * @var string
209 : * @todo check err reporting works
210 : */
211 : var $error = false;
212 : /**
213 : * Possible error messages
214 : * @var array
215 : */
216 : var $error_messages = array(
217 : //GESHI_ERROR_NO_INPUT => 'No source code inputted',
218 : GESHI_ERROR_NO_SUCH_LANG => 'GeSHi could not find the language {LANGUAGE} (using path {PATH})', GESHI_ERROR_FILE_NOT_READABLE => 'The file specified for load_from_file was not readable', GESHI_ERROR_INVALID_HEADER_TYPE => 'The header type specified is invalid', GESHI_ERROR_INVALID_LINE_NUMBER_TYPE => 'The line number type specified is invalid');
219 : /**
220 : * Whether highlighting is strict or not
221 : * @var boolean
222 : */
223 : var $strict_mode = false;
224 : /**
225 : * Whether to use CSS classes in output
226 : * @var boolean
227 : */
228 : var $use_classes = false;
229 : /**
230 : * The type of header to use. Can be one of the following
231 : * values:
232 : *
233 : * <ul>
234 : * <li><b>GESHI_HEADER_PRE</b>: Source is outputted in
235 : * a <pre> HTML element.</li>
236 : * <li><b>GESHI_HEADER_DIV</b>: Source is outputted in
237 : * a <div> HTML element.</li>
238 : * <li><b>GESHI_HEADER_NONE</b>: No header is outputted.</li>
239 : * </ul>
240 : *
241 : * @var int
242 : */
243 : var $header_type = GESHI_HEADER_PRE;
244 : /**
245 : * Array of permissions for which lexics should be highlighted
246 : * @var array
247 : */
248 : var $lexic_permissions = array('KEYWORDS' => array(), 'COMMENTS' => array('MULTI' => true), 'REGEXPS' => array(), 'ESCAPE_CHAR' => true, 'BRACKETS' => true, 'SYMBOLS' => true, 'STRINGS' => true, 'NUMBERS' => true, 'METHODS' => true, 'SCRIPT' => true);
249 : /**
250 : * The time it took to parse the code
251 : * @var double
252 : */
253 : var $time = 0;
254 : /**
255 : * The content of the header block
256 : * @var string
257 : */
258 : var $header_content = '';
259 : /**
260 : * The content of the footer block
261 : * @var string
262 : */
263 : var $footer_content = '';
264 : /**
265 : * The style of the header block
266 : * @var string
267 : */
268 : var $header_content_style = '';
269 : /**
270 : * The style of the footer block
271 : * @var string
272 : */
273 : var $footer_content_style = '';
274 : /**
275 : * The styles for hyperlinks in the code
276 : * @var array
277 : */
278 : var $link_styles = array();
279 : /**
280 : * Whether important blocks should be recognised or not
281 : * @var boolean
282 : * @deprecated
283 : * @todo REMOVE THIS FUNCTIONALITY!
284 : */
285 : var $enable_important_blocks = false;
286 : /**
287 : * Styles for important parts of the code
288 : * @var string
289 : * @deprecated
290 : * @todo As above - rethink the whole idea of important blocks as it is buggy and
291 : * will be hard to implement in 1.2
292 : */
293 : var $important_styles = 'font-weight: bold; color: red;'; // Styles for important parts of the code
294 :
295 : /**
296 : * Whether CSS IDs should be added to the code
297 : * @var boolean
298 : */
299 : var $add_ids = false;
300 : /**
301 : * Lines that should be highlighted extra
302 : * @var array
303 : */
304 : var $highlight_extra_lines = array();
305 : /**
306 : * Styles of extra-highlighted lines
307 : * @var string
308 : */
309 : var $highlight_extra_lines_style = 'color: #cc0; background-color: #ffc;';
310 : /**
311 : * Number at which line numbers should start at
312 : * @var int
313 : * @todo Warning documentation about XHTML compliance
314 : */
315 : var $line_numbers_start = 1;
316 : /**
317 : * The overall style for this code block
318 : * @var string
319 : */
320 : var $overall_style = '';
321 : /**
322 : * The style for the actual code
323 : * @var string
324 : */
325 : var $code_style = 'font-family: \'Courier New\', Courier, monospace; font-weight: normal;';
326 : /**
327 : * The overall class for this code block
328 : * @var string
329 : */
330 : var $overall_class = '';
331 : /**
332 : * The overall ID for this code block
333 : * @var string
334 : */
335 : var $overall_id = '';
336 : /**
337 : * Line number styles
338 : * @var string
339 : */
340 : var $line_style1 = 'font-family: \'Courier New\', Courier, monospace; color: black; font-weight: normal; font-style: normal;';
341 : /**
342 : * Line number styles for fancy lines
343 : * @var string
344 : */
345 : var $line_style2 = 'font-weight: bold;';
346 : /**
347 : * Flag for how line nubmers are displayed
348 : * @var boolean
349 : */
350 : var $line_numbers = GESHI_NO_LINE_NUMBERS;
351 : /**
352 : * The "nth" value for fancy line highlighting
353 : * @var int
354 : */
355 : var $line_nth_row = 0;
356 : /**
357 : * The size of tab stops
358 : * @var int
359 : */
360 : var $tab_width = 8;
361 : /**
362 : * Default target for keyword links
363 : * @var string
364 : */
365 : var $link_target = '';
366 : /**
367 : * The encoding to use for entity encoding
368 : * @var string
369 : */
370 : var $encoding = 'ISO-8859-1';
371 : /**#@-*/
372 : /**
373 : * Creates a new GeSHi object, with source and language
374 : *
375 : * @param string The source code to highlight
376 : * @param string The language to highlight the source with
377 : * @param string The path to the language file directory. <b>This
378 : * is deprecated!</b> I've backported the auto path
379 : * detection from the 1.1.X dev branch, so now it
380 : * should be automatically set correctly. If you have
381 : * renamed the language directory however, you will
382 : * still need to set the path using this parameter or
383 : * {@link GeSHi::set_language_path()}
384 : * @since 1.0.0
385 : */
386 : function GeSHi($source, $language, $path = '') {
387 0 : $this->set_source($source);
388 0 : $this->set_language_path($path);
389 0 : $this->set_language($language);
390 0 : }
391 : /**
392 : * Returns an error message associated with the last GeSHi operation,
393 : * or false if no error has occured
394 : *
395 : * @return string|false An error message if there has been an error, else false
396 : * @since 1.0.0
397 : */
398 : function error() {
399 0 : if ($this->error) {
400 0 : $msg = $this->error_messages[$this->error];
401 0 : $debug_tpl_vars = array('{LANGUAGE}' => $this->language, '{PATH}' => $this->language_path);
402 0 : foreach($debug_tpl_vars as $tpl => $var) {
403 0 : $msg = str_replace($tpl, $var, $msg);
404 0 : }
405 0 : return "<br /><strong>GeSHi Error:</strong> $msg (code $this->error)<br />";
406 : }
407 0 : return false;
408 : }
409 : /**
410 : * Gets a human-readable language name (thanks to Simon Patterson
411 : * for the idea :))
412 : *
413 : * @return string The name for the current language
414 : * @since 1.0.2
415 : */
416 : function get_language_name() {
417 0 : if (GESHI_ERROR_NO_SUCH_LANG == $this->_error) {
418 0 : return $this->language_data['LANG_NAME'].' (Unknown Language)';
419 : }
420 0 : return $this->language_data['LANG_NAME'];
421 : }
422 : /**
423 : * Sets the source code for this object
424 : *
425 : * @param string The source code to highlight
426 : * @since 1.0.0
427 : */
428 : function set_source($source) {
429 0 : $this->source = $source;
430 0 : $this->highlight_extra_lines = array();
431 0 : }
432 : /**
433 : * Sets the language for this object
434 : *
435 : * @param string The name of the language to use
436 : * @since 1.0.0
437 : */
438 : function set_language($language) {
439 0 : $this->error = false;
440 0 : $this->strict_mode = GESHI_NEVER;
441 0 : $language = preg_replace('#[^a-zA-Z0-9\-_]#', '', $language);
442 0 : $this->language = strtolower($language);
443 0 : $file_name = $this->language_path.$this->language.'.php';
444 0 : if (!is_readable($file_name)) {
445 0 : $this->error = GESHI_ERROR_NO_SUCH_LANG;
446 0 : return;
447 : }
448 : // Load the language for parsing
449 0 : $this->load_language($file_name);
450 0 : }
451 : /**
452 : * Sets the path to the directory containing the language files. Note
453 : * that this path is relative to the directory of the script that included
454 : * geshi.php, NOT geshi.php itself.
455 : *
456 : * @param string The path to the language directory
457 : * @since 1.0.0
458 : * @deprecated The path to the language files should now be automatically
459 : * detected, so this method should no longer be needed. The
460 : * 1.1.X branch handles manual setting of the path differently
461 : * so this method will disappear in 1.2.0.
462 : */
463 : function set_language_path($path) {
464 0 : if ($path) {
465 0 : $this->language_path = ('/' == substr($path, strlen($path) -1, 1)) ? $path : $path.'/';
466 0 : $this->set_language($this->language); // otherwise set_language_path has no effect
467 :
468 0 : }
469 0 : }
470 : /**
471 : * Sets the type of header to be used.
472 : *
473 : * If GESHI_HEADER_DIV is used, the code is surrounded in a "div".This
474 : * means more source code but more control over tab width and line-wrapping.
475 : * GESHI_HEADER_PRE means that a "pre" is used - less source, but less
476 : * control. Default is GESHI_HEADER_PRE.
477 : *
478 : * From 1.0.7.2, you can use GESHI_HEADER_NONE to specify that no header code
479 : * should be outputted.
480 : *
481 : * @param int The type of header to be used
482 : * @since 1.0.0
483 : */
484 : function set_header_type($type) {
485 0 : if (GESHI_HEADER_DIV != $type && GESHI_HEADER_PRE != $type && GESHI_HEADER_NONE != $type) {
486 0 : $this->error = GESHI_ERROR_INVALID_HEADER_TYPE;
487 0 : return;
488 : }
489 0 : $this->header_type = $type;
490 : // Set a default overall style if the header is a <div>
491 0 : if (GESHI_HEADER_DIV == $type && !$this->overall_style) {
492 0 : $this->overall_style = 'font-family: monospace;';
493 0 : }
494 0 : }
495 : /**
496 : * Sets the styles for the code that will be outputted
497 : * when this object is parsed. The style should be a
498 : * string of valid stylesheet declarations
499 : *
500 : * @param string The overall style for the outputted code block
501 : * @param boolean Whether to merge the styles with the current styles or not
502 : * @since 1.0.0
503 : */
504 : function set_overall_style($style, $preserve_defaults = false) {
505 0 : if (!$preserve_defaults) {
506 0 : $this->overall_style = $style;
507 0 : } else {
508 0 : $this->overall_style.= $style;
509 : }
510 0 : }
511 : /**
512 : * Sets the overall classname for this block of code. This
513 : * class can then be used in a stylesheet to style this object's
514 : * output
515 : *
516 : * @param string The class name to use for this block of code
517 : * @since 1.0.0
518 : */
519 : function set_overall_class($class) {
520 0 : $this->overall_class = $class;
521 0 : }
522 : /**
523 : * Sets the overall id for this block of code. This id can then
524 : * be used in a stylesheet to style this object's output
525 : *
526 : * @param string The ID to use for this block of code
527 : * @since 1.0.0
528 : */
529 : function set_overall_id($id) {
530 0 : $this->overall_id = $id;
531 0 : }
532 : /**
533 : * Sets whether CSS classes should be used to highlight the source. Default
534 : * is off, calling this method with no arguments will turn it on
535 : *
536 : * @param boolean Whether to turn classes on or not
537 : * @since 1.0.0
538 : */
539 : function enable_classes($flag = true) {
540 0 : $this->use_classes = ($flag) ? true : false;
541 0 : }
542 : /**
543 : * Sets the style for the actual code. This should be a string
544 : * containing valid stylesheet declarations. If $preserve_defaults is
545 : * true, then styles are merged with the default styles, with the
546 : * user defined styles having priority
547 : *
548 : * Note: Use this method to override any style changes you made to
549 : * the line numbers if you are using line numbers, else the line of
550 : * code will have the same style as the line number! Consult the
551 : * GeSHi documentation for more information about this.
552 : *
553 : * @param string The style to use for actual code
554 : * @param boolean Whether to merge the current styles with the new styles
555 : */
556 : function set_code_style($style, $preserve_defaults = false) {
557 0 : if (!$preserve_defaults) {
558 0 : $this->code_style = $style;
559 0 : } else {
560 0 : $this->code_style.= $style;
561 : }
562 0 : }
563 : /**
564 : * Sets the styles for the line numbers.
565 : *
566 : * @param string The style for the line numbers that are "normal"
567 : * @param string|boolean If a string, this is the style of the line
568 : * numbers that are "fancy", otherwise if boolean then this
569 : * defines whether the normal styles should be merged with the
570 : * new normal styles or not
571 : * @param boolean If set, is the flag for whether to merge the "fancy"
572 : * styles with the current styles or not
573 : * @since 1.0.2
574 : */
575 : function set_line_style($style1, $style2 = '', $preserve_defaults = false) {
576 0 : if (is_bool($style2)) {
577 0 : $preserve_defaults = $style2;
578 0 : $style2 = '';
579 0 : }
580 0 : if (!$preserve_defaults) {
581 0 : $this->line_style1 = $style1;
582 0 : $this->line_style2 = $style2;
583 0 : } else {
584 0 : $this->line_style1.= $style1;
585 0 : $this->line_style2.= $style2;
586 : }
587 0 : }
588 : /**
589 : * Sets whether line numbers should be displayed.
590 : *
591 : * Valid values for the first parameter are:
592 : *
593 : * <ul>
594 : * <li><b>GESHI_NO_LINE_NUMBERS</b>: Line numbers will not be displayed</li>
595 : * <li><b>GESHI_NORMAL_LINE_NUMBERS</b>: Line numbers will be displayed</li>
596 : * <li><b>GESHI_FANCY_LINE_NUMBERS</b>: Fancy line numbers will be displayed</li>
597 : * </ul>
598 : *
599 : * For fancy line numbers, the second parameter is used to signal which lines
600 : * are to be fancy. For example, if the value of this parameter is 5 then every
601 : * 5th line will be fancy.
602 : *
603 : * @param int How line numbers should be displayed
604 : * @param int Defines which lines are fancy
605 : * @since 1.0.0
606 : */
607 : function enable_line_numbers($flag, $nth_row = 5) {
608 0 : if (GESHI_NO_LINE_NUMBERS != $flag && GESHI_NORMAL_LINE_NUMBERS != $flag && GESHI_FANCY_LINE_NUMBERS != $flag) {
609 0 : $this->error = GESHI_ERROR_INVALID_LINE_NUMBER_TYPE;
610 0 : }
611 0 : $this->line_numbers = $flag;
612 0 : $this->line_nth_row = $nth_row;
613 0 : }
614 : /**
615 : * Sets the style for a keyword group. If $preserve_defaults is
616 : * true, then styles are merged with the default styles, with the
617 : * user defined styles having priority
618 : *
619 : * @param int The key of the keyword group to change the styles of
620 : * @param string The style to make the keywords
621 : * @param boolean Whether to merge the new styles with the old or just
622 : * to overwrite them
623 : * @since 1.0.0
624 : */
625 : function set_keyword_group_style($key, $style, $preserve_defaults = false) {
626 0 : if (!$preserve_defaults) {
627 0 : $this->language_data['STYLES']['KEYWORDS'][$key] = $style;
628 0 : } else {
629 0 : $this->language_data['STYLES']['KEYWORDS'][$key].= $style;
630 : }
631 0 : }
632 : /**
633 : * Turns highlighting on/off for a keyword group
634 : *
635 : * @param int The key of the keyword group to turn on or off
636 : * @param boolean Whether to turn highlighting for that group on or off
637 : * @since 1.0.0
638 : */
639 : function set_keyword_group_highlighting($key, $flag = true) {
640 0 : $this->lexic_permissions['KEYWORDS'][$key] = ($flag) ? true : false;
641 0 : }
642 : /**
643 : * Sets the styles for comment groups. If $preserve_defaults is
644 : * true, then styles are merged with the default styles, with the
645 : * user defined styles having priority
646 : *
647 : * @param int The key of the comment group to change the styles of
648 : * @param string The style to make the comments
649 : * @param boolean Whether to merge the new styles with the old or just
650 : * to overwrite them
651 : * @since 1.0.0
652 : */
653 : function set_comments_style($key, $style, $preserve_defaults = false) {
654 0 : if (!$preserve_defaults) {
655 0 : $this->language_data['STYLES']['COMMENTS'][$key] = $style;
656 0 : } else {
657 0 : $this->language_data['STYLES']['COMMENTS'][$key].= $style;
658 : }
659 0 : }
660 : /**
661 : * Turns highlighting on/off for comment groups
662 : *
663 : * @param int The key of the comment group to turn on or off
664 : * @param boolean Whether to turn highlighting for that group on or off
665 : * @since 1.0.0
666 : */
667 : function set_comments_highlighting($key, $flag = true) {
668 0 : $this->lexic_permissions['COMMENTS'][$key] = ($flag) ? true : false;
669 0 : }
670 : /**
671 : * Sets the styles for escaped characters. If $preserve_defaults is
672 : * true, then styles are merged with the default styles, with the
673 : * user defined styles having priority
674 : *
675 : * @param string The style to make the escape characters
676 : * @param boolean Whether to merge the new styles with the old or just
677 : * to overwrite them
678 : * @since 1.0.0
679 : */
680 : function set_escape_characters_style($style, $preserve_defaults = false) {
681 0 : if (!$preserve_defaults) {
682 0 : $this->language_data['STYLES']['ESCAPE_CHAR'][0] = $style;
683 0 : } else {
684 0 : $this->language_data['STYLES']['ESCAPE_CHAR'][0].= $style;
685 : }
686 0 : }
687 : /**
688 : * Turns highlighting on/off for escaped characters
689 : *
690 : * @param boolean Whether to turn highlighting for escape characters on or off
691 : * @since 1.0.0
692 : */
693 : function set_escape_characters_highlighting($flag = true) {
694 0 : $this->lexic_permissions['ESCAPE_CHAR'] = ($flag) ? true : false;
695 0 : }
696 : /**
697 : * Sets the styles for brackets. If $preserve_defaults is
698 : * true, then styles are merged with the default styles, with the
699 : * user defined styles having priority
700 : *
701 : * This method is DEPRECATED: use set_symbols_style instead.
702 : * This method will be removed in 1.2.X
703 : *
704 : * @param string The style to make the brackets
705 : * @param boolean Whether to merge the new styles with the old or just
706 : * to overwrite them
707 : * @since 1.0.0
708 : * @deprecated In favour of set_symbols_style
709 : */
710 : function set_brackets_style($style, $preserve_defaults = false) {
711 0 : if (!$preserve_defaults) {
712 0 : $this->language_data['STYLES']['BRACKETS'][0] = $style;
713 0 : } else {
714 0 : $this->language_data['STYLES']['BRACKETS'][0].= $style;
715 : }
716 0 : }
717 : /**
718 : * Turns highlighting on/off for brackets
719 : *
720 : * This method is DEPRECATED: use set_symbols_highlighting instead.
721 : * This method will be remove in 1.2.X
722 : *
723 : * @param boolean Whether to turn highlighting for brackets on or off
724 : * @since 1.0.0
725 : * @deprecated In favour of set_symbols_highlighting
726 : */
727 : function set_brackets_highlighting($flag) {
728 0 : $this->lexic_permissions['BRACKETS'] = ($flag) ? true : false;
729 0 : }
730 : /**
731 : * Sets the styles for symbols. If $preserve_defaults is
732 : * true, then styles are merged with the default styles, with the
733 : * user defined styles having priority
734 : *
735 : * @param string The style to make the symbols
736 : * @param boolean Whether to merge the new styles with the old or just
737 : * to overwrite them
738 : * @since 1.0.1
739 : */
740 : function set_symbols_style($style, $preserve_defaults = false) {
741 0 : if (!$preserve_defaults) {
742 0 : $this->language_data['STYLES']['SYMBOLS'][0] = $style;
743 0 : } else {
744 0 : $this->language_data['STYLES']['SYMBOLS'][0].= $style;
745 : }
746 : // For backward compatibility
747 0 : $this->set_brackets_style($style, $preserve_defaults);
748 0 : }
749 : /**
750 : * Turns highlighting on/off for symbols
751 : *
752 : * @param boolean Whether to turn highlighting for symbols on or off
753 : * @since 1.0.0
754 : */
755 : function set_symbols_highlighting($flag) {
756 0 : $this->lexic_permissions['SYMBOLS'] = ($flag) ? true : false;
757 : // For backward compatibility
758 0 : $this->set_brackets_highlighting($flag);
759 0 : }
760 : /**
761 : * Sets the styles for strings. If $preserve_defaults is
762 : * true, then styles are merged with the default styles, with the
763 : * user defined styles having priority
764 : *
765 : * @param string The style to make the escape characters
766 : * @param boolean Whether to merge the new styles with the old or just
767 : * to overwrite them
768 : * @since 1.0.0
769 : */
770 : function set_strings_style($style, $preserve_defaults = false) {
771 0 : if (!$preserve_defaults) {
772 0 : $this->language_data['STYLES']['STRINGS'][0] = $style;
773 0 : } else {
774 0 : $this->language_data['STYLES']['STRINGS'][0].= $style;
775 : }
776 0 : }
777 : /**
778 : * Turns highlighting on/off for strings
779 : *
780 : * @param boolean Whether to turn highlighting for strings on or off
781 : * @since 1.0.0
782 : */
783 : function set_strings_highlighting($flag) {
784 0 : $this->lexic_permissions['STRINGS'] = ($flag) ? true : false;
785 0 : }
786 : /**
787 : * Sets the styles for numbers. If $preserve_defaults is
788 : * true, then styles are merged with the default styles, with the
789 : * user defined styles having priority
790 : *
791 : * @param string The style to make the numbers
792 : * @param boolean Whether to merge the new styles with the old or just
793 : * to overwrite them
794 : * @since 1.0.0
795 : */
796 : function set_numbers_style($style, $preserve_defaults = false) {
797 0 : if (!$preserve_defaults) {
798 0 : $this->language_data['STYLES']['NUMBERS'][0] = $style;
799 0 : } else {
800 0 : $this->language_data['STYLES']['NUMBERS'][0].= $style;
801 : }
802 0 : }
803 : /**
804 : * Turns highlighting on/off for numbers
805 : *
806 : * @param boolean Whether to turn highlighting for numbers on or off
807 : * @since 1.0.0
808 : */
809 : function set_numbers_highlighting($flag) {
810 0 : $this->lexic_permissions['NUMBERS'] = ($flag) ? true : false;
811 0 : }
812 : /**
813 : * Sets the styles for methods. $key is a number that references the
814 : * appropriate "object splitter" - see the language file for the language
815 : * you are highlighting to get this number. If $preserve_defaults is
816 : * true, then styles are merged with the default styles, with the
817 : * user defined styles having priority
818 : *
819 : * @param int The key of the object splitter to change the styles of
820 : * @param string The style to make the methods
821 : * @param boolean Whether to merge the new styles with the old or just
822 : * to overwrite them
823 : * @since 1.0.0
824 : */
825 : function set_methods_style($key, $style, $preserve_defaults = false) {
826 0 : if (!$preserve_defaults) {
827 0 : $this->language_data['STYLES']['METHODS'][$key] = $style;
828 0 : } else {
829 0 : $this->language_data['STYLES']['METHODS'][$key].= $style;
830 : }
831 0 : }
832 : /**
833 : * Turns highlighting on/off for methods
834 : *
835 : * @param boolean Whether to turn highlighting for methods on or off
836 : * @since 1.0.0
837 : */
838 : function set_methods_highlighting($flag) {
839 0 : $this->lexic_permissions['METHODS'] = ($flag) ? true : false;
840 0 : }
841 : /**
842 : * Sets the styles for regexps. If $preserve_defaults is
843 : * true, then styles are merged with the default styles, with the
844 : * user defined styles having priority
845 : *
846 : * @param string The style to make the regular expression matches
847 : * @param boolean Whether to merge the new styles with the old or just
848 : * to overwrite them
849 : * @since 1.0.0
850 : */
851 : function set_regexps_style($key, $style, $preserve_defaults = false) {
852 0 : if (!$preserve_defaults) {
853 0 : $this->language_data['STYLES']['REGEXPS'][$key] = $style;
854 0 : } else {
855 0 : $this->language_data['STYLES']['REGEXPS'][$key].= $style;
856 : }
857 0 : }
858 : /**
859 : * Turns highlighting on/off for regexps
860 : *
861 : * @param int The key of the regular expression group to turn on or off
862 : * @param boolean Whether to turn highlighting for the regular expression group on or off
863 : * @since 1.0.0
864 : */
865 : function set_regexps_highlighting($key, $flag) {
866 0 : $this->lexic_permissions['REGEXPS'][$key] = ($flag) ? true : false;
867 0 : }
868 : /**
869 : * Sets whether a set of keywords are checked for in a case sensitive manner
870 : *
871 : * @param int The key of the keyword group to change the case sensitivity of
872 : * @param boolean Whether to check in a case sensitive manner or not
873 : * @since 1.0.0
874 : */
875 : function set_case_sensitivity($key, $case) {
876 0 : $this->language_data['CASE_SENSITIVE'][$key] = ($case) ? true : false;
877 0 : }
878 : /**
879 : * Sets the case that keywords should use when found. Use the constants:
880 : *
881 : * <ul>
882 : * <li><b>GESHI_CAPS_NO_CHANGE</b>: leave keywords as-is</li>
883 : * <li><b>GESHI_CAPS_UPPER</b>: convert all keywords to uppercase where found</li>
884 : * <li><b>GESHI_CAPS_LOWER</b>: convert all keywords to lowercase where found</li>
885 : * </ul>
886 : *
887 : * @param int A constant specifying what to do with matched keywords
888 : * @since 1.0.1
889 : * @todo Error check the passed value
890 : */
891 : function set_case_keywords($case) {
892 0 : $this->language_data['CASE_KEYWORDS'] = $case;
893 0 : }
894 : /**
895 : * Sets how many spaces a tab is substituted for
896 : *
897 : * Widths below zero are ignored
898 : *
899 : * @param int The tab width
900 : * @since 1.0.0
901 : */
902 : function set_tab_width($width) {
903 0 : $this->tab_width = intval($width);
904 0 : }
905 : /**
906 : * Enables/disables strict highlighting. Default is off, calling this
907 : * method without parameters will turn it on. See documentation
908 : * for more details on strict mode and where to use it.
909 : *
910 : * @param boolean Whether to enable strict mode or not
911 : * @since 1.0.0
912 : */
913 : function enable_strict_mode($mode = true) {
914 0 : if (GESHI_MAYBE == $this->language_data['STRICT_MODE_APPLIES']) {
915 0 : $this->strict_mode = ($mode) ? true : false;
916 0 : }
917 0 : }
918 : /**
919 : * Disables all highlighting
920 : *
921 : * @since 1.0.0
922 : * @todo Rewrite with an array traversal
923 : */
924 : function disable_highlighting() {
925 0 : foreach($this->lexic_permissions as $key => $value) {
926 0 : if (is_array($value)) {
927 0 : foreach($value as $k => $v) {
928 0 : $this->lexic_permissions[$key][$k] = false;
929 0 : }
930 0 : } else {
931 0 : $this->lexic_permissions[$key] = false;
932 : }
933 0 : }
934 : // Context blocks
935 0 : $this->enable_important_blocks = false;
936 0 : }
937 : /**
938 : * Enables all highlighting
939 : *
940 : * @since 1.0.0
941 : * @todo Rewrite with array traversal
942 : */
943 : function enable_highlighting() {
944 0 : foreach($this->lexic_permissions as $key => $value) {
945 0 : if (is_array($value)) {
946 0 : foreach($value as $k => $v) {
947 0 : $this->lexic_permissions[$key][$k] = true;
948 0 : }
949 0 : } else {
950 0 : $this->lexic_permissions[$key] = true;
951 : }
952 0 : }
953 : // Context blocks
954 0 : $this->enable_important_blocks = true;
955 0 : }
956 : /**
957 : * Given a file extension, this method returns either a valid geshi language
958 : * name, or the empty string if it couldn't be found
959 : *
960 : * @param string The extension to get a language name for
961 : * @param array A lookup array to use instead of the default
962 : * @since 1.0.5
963 : * @todo Re-think about how this method works (maybe make it private and/or make it
964 : * a extension->lang lookup?)
965 : * @todo static?
966 : */
967 : function get_language_name_from_extension($extension, $lookup = array()) {
968 0 : if (!$lookup) {
969 0 : $lookup = array('actionscript' => array('as'), 'ada' => array('a', 'ada', 'adb', 'ads'), 'apache' => array('conf'), 'asm' => array('ash', 'asm'), 'asp' => array('asp'), 'bash' => array('sh'), 'c' => array('c'), 'c_mac' => array('c'), 'caddcl' => array(), 'cadlisp' => array(), 'cdfg' => array('cdfg'), 'cpp' => array('cpp'), 'csharp' => array(), 'css' => array('css'), 'delphi' => array('dpk', 'dpr'), 'html4strict' => array('html', 'htm'), 'java' => array('java'), 'javascript' => array('js'), 'lisp' => array('lisp'), 'lua' => array('lua'), 'mpasm' => array(), 'nsis' => array(), 'objc' => array(), 'oobas' => array(), 'oracle8' => array(), 'pascal' => array('pas'), 'perl' => array('pl', 'pm'), 'php' => array('php', 'php5', 'phtml', 'phps'), 'python' => array('py'), 'qbasic' => array('bi'), 'sas' => array('sas'), 'smarty' => array(), 'vb' => array('bas'), 'vbnet' => array(), 'visualfoxpro' => array(), 'xml' => array('xml'));
970 0 : }
971 0 : foreach($lookup as $lang => $extensions) {
972 0 : foreach($extensions as $ext) {
973 0 : if ($ext == $extension) {
974 0 : return $lang;
975 : }
976 0 : }
977 0 : }
978 0 : return '';
979 : }
980 : /**
981 : * Given a file name, this method loads its contents in, and attempts
982 : * to set the language automatically. An optional lookup table can be
983 : * passed for looking up the language name. If not specified a default
984 : * table is used
985 : *
986 : * The language table is in the form
987 : * <pre>array(
988 : * 'lang_name' => array('extension', 'extension', ...),
989 : * 'lang_name' ...
990 : * );</pre>
991 : *
992 : * @todo Complete rethink of this and above method
993 : * @since 1.0.5
994 : */
995 : function load_from_file($file_name, $lookup = array()) {
996 0 : if (is_readable($file_name)) {
997 0 : $this->set_source(implode('', file($file_name)));
998 0 : $this->set_language($this->get_language_name_from_extension(substr(strrchr($file_name, '.'), 1), $lookup));
999 0 : } else {
1000 0 : $this->error = GESHI_ERROR_FILE_NOT_READABLE;
1001 : }
1002 0 : }
1003 : /**
1004 : * Adds a keyword to a keyword group for highlighting
1005 : *
1006 : * @param int The key of the keyword group to add the keyword to
1007 : * @param string The word to add to the keyword group
1008 : * @since 1.0.0
1009 : */
1010 : function add_keyword($key, $word) {
1011 0 : $this->language_data['KEYWORDS'][$key][] = $word;
1012 0 : }
1013 : /**
1014 : * Removes a keyword from a keyword group
1015 : *
1016 : * @param int The key of the keyword group to remove the keyword from
1017 : * @param string The word to remove from the keyword group
1018 : * @since 1.0.0
1019 : */
1020 : function remove_keyword($key, $word) {
1021 0 : $this->language_data['KEYWORDS'][$key] = array_diff($this->language_data['KEYWORDS'][$key], array($word));
1022 0 : }
1023 : /**
1024 : * Creates a new keyword group
1025 : *
1026 : * @param int The key of the keyword group to create
1027 : * @param string The styles for the keyword group
1028 : * @param boolean Whether the keyword group is case sensitive ornot
1029 : * @param array The words to use for the keyword group
1030 : * @since 1.0.0
1031 : */
1032 : function add_keyword_group($key, $styles, $case_sensitive = true, $words = array()) {
1033 0 : $words = (array)$words;
1034 0 : $this->language_data['KEYWORDS'][$key] = $words;
1035 0 : $this->lexic_permissions['KEYWORDS'][$key] = true;
1036 0 : $this->language_data['CASE_SENSITIVE'][$key] = $case_sensitive;
1037 0 : $this->language_data['STYLES']['KEYWORDS'][$key] = $styles;
1038 0 : }
1039 : /**
1040 : * Removes a keyword group
1041 : *
1042 : * @param int The key of the keyword group to remove
1043 : * @since 1.0.0
1044 : */
1045 : function remove_keyword_group($key) {
1046 0 : unset($this->language_data['KEYWORDS'][$key]);
1047 0 : unset($this->lexic_permissions['KEYWORDS'][$key]);
1048 0 : unset($this->language_data['CASE_SENSITIVE'][$key]);
1049 0 : unset($this->language_data['STYLES']['KEYWORDS'][$key]);
1050 0 : }
1051 : /**
1052 : * Sets the content of the header block
1053 : *
1054 : * @param string The content of the header block
1055 : * @since 1.0.2
1056 : */
1057 : function set_header_content($content) {
1058 0 : $this->header_content = $content;
1059 0 : }
1060 : /**
1061 : * Sets the content of the footer block
1062 : *
1063 : * @param string The content of the footer block
1064 : * @since 1.0.2
1065 : */
1066 : function set_footer_content($content) {
1067 0 : $this->footer_content = $content;
1068 0 : }
1069 : /**
1070 : * Sets the style for the header content
1071 : *
1072 : * @param string The style for the header content
1073 : * @since 1.0.2
1074 : */
1075 : function set_header_content_style($style) {
1076 0 : $this->header_content_style = $style;
1077 0 : }
1078 : /**
1079 : * Sets the style for the footer content
1080 : *
1081 : * @param string The style for the footer content
1082 : * @since 1.0.2
1083 : */
1084 : function set_footer_content_style($style) {
1085 0 : $this->footer_content_style = $style;
1086 0 : }
1087 : /**
1088 : * Sets the base URL to be used for keywords
1089 : *
1090 : * @param int The key of the keyword group to set the URL for
1091 : * @param string The URL to set for the group. If {FNAME} is in
1092 : * the url somewhere, it is replaced by the keyword
1093 : * that the URL is being made for
1094 : * @since 1.0.2
1095 : */
1096 : function set_url_for_keyword_group($group, $url) {
1097 0 : $this->language_data['URLS'][$group] = $url;
1098 0 : }
1099 : /**
1100 : * Sets styles for links in code
1101 : *
1102 : * @param int A constant that specifies what state the style is being
1103 : * set for - e.g. :hover or :visited
1104 : * @param string The styles to use for that state
1105 : * @since 1.0.2
1106 : */
1107 : function set_link_styles($type, $styles) {
1108 0 : $this->link_styles[$type] = $styles;
1109 0 : }
1110 : /**
1111 : * Sets the target for links in code
1112 : *
1113 : * @param string The target for links in the code, e.g. _blank
1114 : * @since 1.0.3
1115 : */
1116 : function set_link_target($target) {
1117 0 : if (!$target) {
1118 0 : $this->link_target = '';
1119 0 : } else {
1120 0 : $this->link_target = ' target="'.$target.'" ';
1121 : }
1122 0 : }
1123 : /**
1124 : * Sets styles for important parts of the code
1125 : *
1126 : * @param string The styles to use on important parts of the code
1127 : * @since 1.0.2
1128 : */
1129 : function set_important_styles($styles) {
1130 0 : $this->important_styles = $styles;
1131 0 : }
1132 : /**
1133 : * Sets whether context-important blocks are highlighted
1134 : *
1135 : * @todo REMOVE THIS SHIZ FROM GESHI!
1136 : * @deprecated
1137 : */
1138 : function enable_important_blocks($flag) {
1139 0 : $this->enable_important_blocks = ($flag) ? true : false;
1140 0 : }
1141 : /**
1142 : * Whether CSS IDs should be added to each line
1143 : *
1144 : * @param boolean If true, IDs will be added to each line.
1145 : * @since 1.0.2
1146 : */
1147 : function enable_ids($flag = true) {
1148 0 : $this->add_ids = ($flag) ? true : false;
1149 0 : }
1150 : /**
1151 : * Specifies which lines to highlight extra
1152 : *
1153 : * @param mixed An array of line numbers to highlight, or just a line
1154 : * number on its own.
1155 : * @since 1.0.2
1156 : * @todo Some data replication here that could be cut down on
1157 : */
1158 : function highlight_lines_extra($lines) {
1159 0 : if (is_array($lines)) {
1160 0 : foreach($lines as $line) {
1161 0 : $this->highlight_extra_lines[intval($line) ] = intval($line);
1162 0 : }
1163 0 : } else {
1164 0 : $this->highlight_extra_lines[intval($lines) ] = intval($lines);
1165 : }
1166 0 : }
1167 : /**
1168 : * Sets the style for extra-highlighted lines
1169 : *
1170 : * @param string The style for extra-highlighted lines
1171 : * @since 1.0.2
1172 : */
1173 : function set_highlight_lines_extra_style($styles) {
1174 0 : $this->highlight_extra_lines_style = $styles;
1175 0 : }
1176 : /**
1177 : * Sets what number line numbers should start at. Should
1178 : * be a positive integer, and will be converted to one.
1179 : *
1180 : * <b>Warning:</b> Using this method will add the "start"
1181 : * attribute to the <ol> that is used for line numbering.
1182 : * This is <b>not</b> valid XHTML strict, so if that's what you
1183 : * care about then don't use this method. Firefox is getting
1184 : * support for the CSS method of doing this in 1.1 and Opera
1185 : * has support for the CSS method, but (of course) IE doesn't
1186 : * so it's not worth doing it the CSS way yet.
1187 : *
1188 : * @param int The number to start line numbers at
1189 : * @since 1.0.2
1190 : */
1191 : function start_line_numbers_at($number) {
1192 0 : $this->line_numbers_start = abs(intval($number));
1193 0 : }
1194 : /**
1195 : * Sets the encoding used for htmlspecialchars(), for international
1196 : * support.
1197 : *
1198 : * @param string The encoding to use for the source
1199 : * @since 1.0.3
1200 : */
1201 : function set_encoding($encoding) {
1202 0 : if ($encoding) {
1203 0 : $this->encoding = $encoding;
1204 0 : }
1205 0 : }
1206 : /**
1207 : * Returns the code in $this->source, highlighted and surrounded by the
1208 : * nessecary HTML.
1209 : *
1210 : * This should only be called ONCE, cos it's SLOW! If you want to highlight
1211 : * the same source multiple times, you're better off doing a whole lot of
1212 : * str_replaces to replace the <span>s
1213 : *
1214 : * @since 1.0.0
1215 : */
1216 : function parse_code() {
1217 : // Start the timer
1218 0 : $start_time = microtime();
1219 : // Firstly, if there is an error, we won't highlight
1220 0 : if ($this->error) {
1221 0 : $result = @htmlspecialchars($this->source, ENT_COMPAT, $this->encoding);
1222 : // Timing is irrelevant
1223 0 : $this->set_time($start_time, $start_time);
1224 0 : return $this->finalise($result);
1225 : }
1226 : // Replace all newlines to a common form.
1227 0 : $code = str_replace("\r\n", "\n", $this->source);
1228 0 : $code = str_replace("\r", "\n", $code);
1229 : // Add spaces for regular expression matching and line numbers
1230 0 : $code = "\n".$code."\n";
1231 : // Initialise various stuff
1232 0 : $length = strlen($code);
1233 0 : $STRING_OPEN = '';
1234 0 : $CLOSE_STRING = false;
1235 0 : $ESCAPE_CHAR_OPEN = false;
1236 0 : $COMMENT_MATCHED = false;
1237 : // Turn highlighting on if strict mode doesn't apply to this language
1238 0 : $HIGHLIGHTING_ON = (!$this->strict_mode) ? true : '';
1239 : // Whether to highlight inside a block of code
1240 0 : $HIGHLIGHT_INSIDE_STRICT = false;
1241 0 : $HARDQUOTE_OPEN = false;
1242 0 : $stuff_to_parse = '';
1243 0 : $result = '';
1244 : // "Important" selections are handled like multiline comments
1245 : // @todo GET RID OF THIS SHIZ
1246 0 : if ($this->enable_important_blocks) {
1247 0 : $this->language_data['COMMENT_MULTI'][GESHI_START_IMPORTANT] = GESHI_END_IMPORTANT;
1248 0 : }
1249 0 : if ($this->strict_mode) {
1250 : // Break the source into bits. Each bit will be a portion of the code
1251 : // within script delimiters - for example, HTML between < and >
1252 0 : $parts = array(0 => array(0 => ''));
1253 0 : $k = 0;
1254 0 : for ($i = 0 ; $i < $length ; $i++) {
1255 0 : $char = substr($code, $i, 1);
1256 0 : if (!$HIGHLIGHTING_ON) {
1257 0 : foreach($this->language_data['SCRIPT_DELIMITERS'] as $key => $delimiters) {
1258 0 : foreach($delimiters as $open => $close) {
1259 : // Get the next little bit for this opening string
1260 0 : $check = substr($code, $i, strlen($open));
1261 : // If it matches...
1262 0 : if ($check == $open) {
1263 : // We start a new block with the highlightable
1264 : // code in it
1265 0 : $HIGHLIGHTING_ON = $open;
1266 0 : $i+= strlen($open) -1;
1267 0 : $char = $open;
1268 0 : $parts[++$k][0] = $char;
1269 : // No point going around again...
1270 0 : break(2);
1271 : }
1272 0 : }
1273 0 : }
1274 0 : } else {
1275 0 : foreach($this->language_data['SCRIPT_DELIMITERS'] as $key => $delimiters) {
1276 0 : foreach($delimiters as $open => $close) {
1277 0 : if ($open == $HIGHLIGHTING_ON) {
1278 : // Found the closing tag
1279 0 : break(2);
1280 : }
1281 0 : }
1282 0 : }
1283 : // We check code from our current position BACKWARDS. This is so
1284 : // the ending string for highlighting can be included in the block
1285 0 : $check = substr($code, $i-strlen($close) +1, strlen($close));
1286 0 : if ($check == $close) {
1287 0 : $HIGHLIGHTING_ON = '';
1288 : // Add the string to the rest of the string for this part
1289 0 : $parts[$k][1] = (isset($parts[$k][1])) ? $parts[$k][1].$char : $char;
1290 0 : $parts[++$k][0] = '';
1291 0 : $char = '';
1292 0 : }
1293 : }
1294 0 : $parts[$k][1] = (isset($parts[$k][1])) ? $parts[$k][1].$char : $char;
1295 0 : }
1296 0 : $HIGHLIGHTING_ON = '';
1297 0 : } else {
1298 : // Not strict mode - simply dump the source into
1299 : // the array at index 1 (the first highlightable block)
1300 0 : $parts = array(1 => array(0 => '', 1 => $code));
1301 : }
1302 : // Now we go through each part. We know that even-indexed parts are
1303 : // code that shouldn't be highlighted, and odd-indexed parts should
1304 : // be highlighted
1305 0 : foreach($parts as $key => $data) {
1306 0 : $part = $data[1];
1307 : // If this block should be highlighted...
1308 0 : if ($key%2) {
1309 0 : if ($this->strict_mode) {
1310 : // Find the class key for this block of code
1311 0 : foreach($this->language_data['SCRIPT_DELIMITERS'] as $script_key => $script_data) {
1312 0 : foreach($script_data as $open => $close) {
1313 0 : if ($data[0] == $open) {
1314 0 : break(2);
1315 : }
1316 0 : }
1317 0 : }
1318 0 : if ($this->language_data['STYLES']['SCRIPT'][$script_key] != '' && $this->lexic_permissions['SCRIPT']) {
1319 : // Add a span element around the source to
1320 : // highlight the overall source block
1321 0 : if (!$this->use_classes && $this->language_data['STYLES']['SCRIPT'][$script_key] != '') {
1322 0 : $attributes = ' style="'.$this->language_data['STYLES']['SCRIPT'][$script_key].'"';
1323 0 : } else {
1324 0 : $attributes = ' class="sc'.$script_key.'"';
1325 : }
1326 0 : $result.= "<span$attributes>";
1327 0 : }
1328 0 : }
1329 0 : if (!$this->strict_mode || $this->language_data['HIGHLIGHT_STRICT_BLOCK'][$script_key]) {
1330 : // Now, highlight the code in this block. This code
1331 : // is really the engine of GeSHi (along with the method
1332 : // parse_non_string_part).
1333 0 : $length = strlen($part);
1334 0 : for ($i = 0 ; $i < $length ; $i++) {
1335 : // Get the next char
1336 0 : $char = substr($part, $i, 1);
1337 0 : $hq = isset($this->language_data['HARDQUOTE']) ? $this->language_data['HARDQUOTE'][0] : false;
1338 : // Is this char the newline and line numbers being used?
1339 0 : if (($this->line_numbers != GESHI_NO_LINE_NUMBERS || count($this->highlight_extra_lines) > 0) && $char == "\n") {
1340 : // If so, is there a string open? If there is, we should end it before
1341 : // the newline and begin it again (so when <li>s are put in the source
1342 : // remains XHTML compliant)
1343 : // note to self: This opens up possibility of config files specifying
1344 : // that languages can/cannot have multiline strings???
1345 0 : if ($STRING_OPEN) {
1346 0 : if (!$this->use_classes) {
1347 0 : $attributes = ' style="'.$this->language_data['STYLES']['STRINGS'][0].'"';
1348 0 : } else {
1349 0 : $attributes = ' class="st0"';
1350 : }
1351 0 : $char = '</span>'.$char."<span$attributes>";
1352 0 : }
1353 0 : } elseif ($char == $STRING_OPEN) {
1354 : // A match of a string delimiter
1355 0 : if (($this->lexic_permissions['ESCAPE_CHAR'] && $ESCAPE_CHAR_OPEN) || ($this->lexic_permissions['STRINGS'] && !$ESCAPE_CHAR_OPEN)) {
1356 0 : $char = htmlspecialchars($char, ENT_COMPAT, $this->encoding) .'</span>';
1357 0 : }
1358 0 : $escape_me = false;
1359 0 : if ($HARDQUOTE_OPEN) {
1360 0 : if ($ESCAPE_CHAR_OPEN) $escape_me = true;
1361 : else {
1362 0 : foreach($this->language_data['HARDESCAPE'] as $hardesc) if (substr($part, $i, strlen($hardesc)) == $hardesc) {
1363 0 : $escape_me = true;
1364 0 : break;
1365 : }
1366 0 : }
1367 0 : }
1368 0 : if (!$ESCAPE_CHAR_OPEN) {
1369 0 : $STRING_OPEN = '';
1370 0 : $CLOSE_STRING = true;
1371 0 : }
1372 0 : if (!$escape_me) {
1373 0 : $HARDQUOTE_OPEN = false;
1374 0 : }
1375 0 : $ESCAPE_CHAR_OPEN = false;
1376 0 : } elseif (in_array($char, $this->language_data['QUOTEMARKS']) && ($STRING_OPEN == '') && $this->lexic_permissions['STRINGS']) {
1377 : // The start of a new string
1378 0 : $STRING_OPEN = $char;
1379 0 : if (!$this->use_classes) {
1380 0 : $attributes = ' style="'.$this->language_data['STYLES']['STRINGS'][0].'"';
1381 0 : } else {
1382 0 : $attributes = ' class="st0"';
1383 : }
1384 0 : $char = "<span$attributes>".htmlspecialchars($char, ENT_COMPAT, $this->encoding);
1385 0 : $result.= $this->parse_non_string_part($stuff_to_parse);
1386 0 : $stuff_to_parse = '';
1387 0 : } elseif ($hq && substr($part, $i, strlen($hq)) == $hq && ($STRING_OPEN == '') && $this->lexic_permissions['STRINGS']) {
1388 : // The start of a hard quoted string
1389 0 : $STRING_OPEN = $this->language_data['HARDQUOTE'][1];
1390 0 : if (!$this->use_classes) {
1391 0 : $attributes = ' style="'.$this->language_data['STYLES']['STRINGS'][0].'"';
1392 0 : } else {
1393 0 : $attributes = ' class="st0"';
1394 : }
1395 0 : $char = "<span$attributes>".$hq;
1396 0 : $i+= strlen($hq) -1;
1397 0 : $HARDQUOTE_OPEN = true;
1398 0 : $result.= $this->parse_non_string_part($stuff_to_parse);
1399 0 : $stuff_to_parse = '';
1400 0 : } elseif ($char == $this->language_data['ESCAPE_CHAR'] && $STRING_OPEN != '') {
1401 : // An escape character
1402 0 : if (!$ESCAPE_CHAR_OPEN) {
1403 0 : $ESCAPE_CHAR_OPEN = !$HARDQUOTE_OPEN; // true unless $HARDQUOTE_OPEN
1404 0 : if ($HARDQUOTE_OPEN) foreach($this->language_data['HARDESCAPE'] as $hard) {
1405 0 : if (substr($part, $i, strlen($hard)) == $hard) {
1406 0 : $ESCAPE_CHAR_OPEN = true;
1407 0 : break;
1408 : }
1409 0 : }
1410 0 : if ($ESCAPE_CHAR_OPEN && $this->lexic_permissions['ESCAPE_CHAR']) {
1411 0 : if (!$this->use_classes) {
1412 0 : $attributes = ' style="'.$this->language_data['STYLES']['ESCAPE_CHAR'][0].'"';
1413 0 : } else {
1414 0 : $attributes = ' class="es0"';
1415 : }
1416 0 : $char = "<span$attributes>".$char;
1417 0 : if (substr($code, $i+1, 1) == "\n") {
1418 : // escaping a newline, what's the point in putting the span around
1419 : // the newline? It only causes hassles when inserting line numbers
1420 0 : $char.= '</span>';
1421 0 : $ESCAPE_CHAR_OPEN = false;
1422 0 : }
1423 0 : }
1424 0 : } else {
1425 0 : $ESCAPE_CHAR_OPEN = false;
1426 0 : if ($this->lexic_permissions['ESCAPE_CHAR']) {
1427 0 : $char.= '</span>';
1428 0 : }
1429 : }
1430 0 : } elseif ($ESCAPE_CHAR_OPEN) {
1431 0 : if ($this->lexic_permissions['ESCAPE_CHAR']) {
1432 0 : $char.= '</span>';
1433 0 : }
1434 0 : $ESCAPE_CHAR_OPEN = false;
1435 0 : $test_str = $char;
1436 0 : } elseif ($STRING_OPEN == '') {
1437 : // Is this a multiline comment?
1438 0 : foreach($this->language_data['COMMENT_MULTI'] as $open => $close) {
1439 0 : $com_len = strlen($open);
1440 0 : $test_str = substr($part, $i, $com_len);
1441 0 : $test_str_match = $test_str;
1442 0 : if ($open == $test_str) {
1443 0 : $COMMENT_MATCHED = true;
1444 : //@todo If remove important do remove here
1445 0 : if ($this->lexic_permissions['COMMENTS']['MULTI'] || $test_str == GESHI_START_IMPORTANT) {
1446 0 : if ($test_str != GESHI_START_IMPORTANT) {
1447 0 : if (!$this->use_classes) {
1448 0 : $attributes = ' style="'.$this->language_data['STYLES']['COMMENTS']['MULTI'].'"';
1449 0 : } else {
1450 0 : $attributes = ' class="coMULTI"';
1451 : }
1452 0 : $test_str = "<span$attributes>".@htmlspecialchars($test_str, ENT_COMPAT, $this->encoding);
1453 0 : } else {
1454 0 : if (!$this->use_classes) {
1455 0 : $attributes = ' style="'.$this->important_styles.'"';
1456 0 : } else {
1457 0 : $attributes = ' class="imp"';
1458 : }
1459 : // We don't include the start of the comment if it's an
1460 : // "important" part
1461 0 : $test_str = "<span$attributes>";
1462 : }
1463 0 : } else {
1464 0 : $test_str = @htmlspecialchars($test_str, ENT_COMPAT, $this->encoding);
1465 : }
1466 0 : $close_pos = strpos($part, $close, $i+strlen($close));
1467 0 : if ($close_pos === false) {
1468 0 : $close_pos = strlen($part);
1469 0 : }
1470 : // Short-cut through all the multiline code
1471 0 : $rest_of_comment = @htmlspecialchars(substr($part, $i+$com_len, $close_pos-$i), ENT_COMPAT, $this->encoding);
1472 0 : if (($this->lexic_permissions['COMMENTS']['MULTI'] || $test_str_match == GESHI_START_IMPORTANT) && ($this->line_numbers != GESHI_NO_LINE_NUMBERS || count($this->highlight_extra_lines) > 0)) {
1473 : // strreplace to put close span and open span around multiline newlines
1474 0 : $test_str.= str_replace("\n", "</span>\n<span$attributes>", $rest_of_comment);
1475 0 : } else {
1476 0 : $test_str.= $rest_of_comment;
1477 : }
1478 0 : if ($this->lexic_permissions['COMMENTS']['MULTI'] || $test_str_match == GESHI_START_IMPORTANT) {
1479 0 : $test_str.= '</span>';
1480 0 : }
1481 0 : $i = $close_pos+$com_len-1;
1482 : // parse the rest
1483 0 : $result.= $this->parse_non_string_part($stuff_to_parse);
1484 0 : $stuff_to_parse = '';
1485 0 : break;
1486 : }
1487 0 : }
1488 : // If we haven't matched a multiline comment, try single-line comments
1489 0 : if (!$COMMENT_MATCHED) {
1490 0 : foreach($this->language_data['COMMENT_SINGLE'] as $comment_key => $comment_mark) {
1491 0 : $com_len = strlen($comment_mark);
1492 0 : $test_str = substr($part, $i, $com_len);
1493 0 : if ($this->language_data['CASE_SENSITIVE'][GESHI_COMMENTS]) {
1494 0 : $match = ($comment_mark == $test_str);
1495 0 : } else {
1496 0 : $match = (strtolower($comment_mark) == strtolower($test_str));
1497 : }
1498 0 : if ($match) {
1499 0 : $COMMENT_MATCHED = true;
1500 0 : if ($this->lexic_permissions['COMMENTS'][$comment_key]) {
1501 0 : if (!$this->use_classes) {
1502 0 : $attributes = ' style="'.$this->language_data['STYLES']['COMMENTS'][$comment_key].'"';
1503 0 : } else {
1504 0 : $attributes = ' class="co'.$comment_key.'"';
1505 : }
1506 0 : $test_str = "<span$attributes>".@htmlspecialchars($this->change_case($test_str), ENT_COMPAT, $this->encoding);
1507 0 : } else {
1508 0 : $test_str = @htmlspecialchars($test_str, ENT_COMPAT, $this->encoding);
1509 : }
1510 0 : $close_pos = strpos($part, "\n", $i);
1511 0 : $oops = false;
1512 0 : if ($close_pos === false) {
1513 0 : $close_pos = strlen($part);
1514 0 : $oops = true;
1515 0 : }
1516 0 : $test_str.= @htmlspecialchars(substr($part, $i+$com_len, $close_pos-$i-$com_len), ENT_COMPAT, $this->encoding);
1517 0 : if ($this->lexic_permissions['COMMENTS'][$comment_key]) {
1518 0 : $test_str.= "</span>";
1519 0 : }
1520 : // Take into account that the comment might be the last in the source
1521 0 : if (!$oops) {
1522 0 : $test_str.= "\n";
1523 0 : }
1524 0 : $i = $close_pos;
1525 : // parse the rest
1526 0 : $result.= $this->parse_non_string_part($stuff_to_parse);
1527 0 : $stuff_to_parse = '';
1528 0 : break;
1529 : }
1530 0 : }
1531 0 : }
1532 0 : } elseif ($STRING_OPEN != '') {
1533 : // Otherwise, convert it to HTML form
1534 0 : if (strtolower($this->encoding) == 'utf-8') {
1535 : //only escape <128 (we don't want to break multibyte chars)
1536 0 : if (ord($char) < 128) {
1537 0 : $char = @htmlspecialchars($char, ENT_COMPAT, $this->encoding);
1538 0 : }
1539 0 : } else {
1540 : //encode everthing
1541 0 : $char = @htmlspecialchars($char, ENT_COMPAT, $this->encoding);
1542 : }
1543 0 : }
1544 : // Where are we adding this char?
1545 0 : if (!$COMMENT_MATCHED) {
1546 0 : if (($STRING_OPEN == '') && !$CLOSE_STRING) {
1547 0 : $stuff_to_parse.= $char;
1548 0 : } else {
1549 0 : $result.= $char;
1550 0 : $CLOSE_STRING = false;
1551 : }
1552 0 : } else {
1553 0 : $result.= $test_str;
1554 0 : $COMMENT_MATCHED = false;
1555 : }
1556 0 : }
1557 : // Parse the last bit
1558 0 : $result.= $this->parse_non_string_part($stuff_to_parse);
1559 0 : $stuff_to_parse = '';
1560 0 : } else {
1561 0 : $result.= @htmlspecialchars($part, ENT_COMPAT, $this->encoding);
1562 : }
1563 : // Close the <span> that surrounds the block
1564 0 : if ($this->strict_mode && $this->language_data['STYLES']['SCRIPT'][$script_key] != '' && $this->lexic_permissions['SCRIPT']) {
1565 0 : $result.= '</span>';
1566 0 : }
1567 0 : } else {
1568 : // Else not a block to highlight
1569 0 : $result.= @htmlspecialchars($part, ENT_COMPAT, $this->encoding);
1570 : }
1571 0 : }
1572 : // Parse the last stuff (redundant?)
1573 0 : $result.= $this->parse_non_string_part($stuff_to_parse);
1574 : // Lop off the very first and last spaces
1575 0 : $result = substr($result, 1, -1);
1576 : // Are we still in a string?
1577 0 : if ($STRING_OPEN) {
1578 0 : $result.= '</span>';
1579 0 : }
1580 : // We're finished: stop timing
1581 0 : $this->set_time($start_time, microtime());
1582 0 : return $this->finalise($result);
1583 : }
1584 : /**
1585 : * Swaps out spaces and tabs for HTML indentation. Not needed if
1586 : * the code is in a pre block...
1587 : *
1588 : * @param string The source to indent
1589 : * @return string The source with HTML indenting applied
1590 : * @since 1.0.0
1591 : * @access private
1592 : */
1593 : function indent($result) {
1594 : /// Replace tabs with the correct number of spaces
1595 0 : if (false !== strpos($result, "\t")) {
1596 0 : $lines = explode("\n", $result);
1597 0 : foreach($lines as $key => $line) {
1598 0 : if (false === strpos($line, "\t")) {
1599 0 : $lines[$key] = $line;
1600 0 : continue;
1601 : }
1602 0 : $pos = 0;
1603 0 : $tab_width = $this->tab_width;
1604 0 : $length = strlen($line);
1605 0 : $result_line = '';
1606 0 : $IN_TAG = false;
1607 0 : for ($i = 0 ; $i < $length ; $i++) {
1608 0 : $char = substr($line, $i, 1);
1609 : // Simple engine to work out whether we're in a tag.
1610 : // If we are we modify $pos. This is so we ignore HTML
1611 : // in the line and only workout the tab replacement
1612 : // via the actual content of the string
1613 : // This test could be improved to include strings in the
1614 : // html so that < or > would be allowed in user's styles
1615 : // (e.g. quotes: '<' '>'; or similar)
1616 0 : if ($IN_TAG && '>' == $char) {
1617 0 : $IN_TAG = false;
1618 0 : $result_line.= '>';
1619 0 : ++$pos;
1620 0 : } elseif (!$IN_TAG && '<' == $char) {
1621 0 : $IN_TAG = true;
1622 0 : $result_line.= '<';
1623 0 : ++$pos;
1624 0 : } elseif (!$IN_TAG && '&' == $char) {
1625 0 : $substr = substr($line, $i+3, 4);
1626 : //$substr_5 = substr($line, 5, 1);
1627 0 : $posi = strpos($substr, ';');
1628 0 : if (false !== $posi) {
1629 0 : $pos+= $posi+3;
1630 0 : }
1631 0 : $result_line.= '&';
1632 0 : } elseif (!$IN_TAG && "\t" == $char) {
1633 0 : $str = '';
1634 : // OPTIMISE - move $strs out. Make an array:
1635 : // $tabs = array(
1636 : // 1 => ' ',
1637 : // 2 => ' ',
1638 : // 3 => ' ' etc etc
1639 : // to use instead of building a string every time
1640 0 : $strs = array(0 => ' ', 1 => ' ');
1641 0 : for ($k = 0 ; $k < ($tab_width-(($i-$pos) %$tab_width)) ; $k++) $str.= $strs[$k%2];
1642 0 : $result_line.= $str;
1643 : //$pos--;
1644 0 : $pos++;
1645 : //$pos -= $tab_width-1;
1646 0 : if (false === strpos($line, "\t", $i+1)) {
1647 : //$lines[$key] = $result_line;
1648 0 : $result_line.= substr($line, $i+1);
1649 0 : break;
1650 : }
1651 0 : } elseif ($IN_TAG) {
1652 0 : ++$pos;
1653 0 : $result_line.= $char;
1654 0 : } else {
1655 0 : $result_line.= $char;
1656 : //++$pos;
1657 :
1658 : }
1659 0 : }
1660 0 : $lines[$key] = $result_line;
1661 0 : }
1662 0 : $result = implode("\n", $lines);
1663 0 : }
1664 : // Other whitespace
1665 0 : $result = str_replace(' ', ' ', $result);
1666 0 : $result = str_replace(' ', ' ', $result);
1667 0 : $result = str_replace("\n ", "\n ", $result);
1668 0 : if ($this->line_numbers == GESHI_NO_LINE_NUMBERS) {
1669 0 : $result = nl2br($result);
1670 0 : }
1671 0 : return $result;
1672 : }
1673 : /**
1674 : * Changes the case of a keyword for those languages where a change is asked for
1675 : *
1676 : * @param string The keyword to change the case of
1677 : * @return string The keyword with its case changed
1678 : * @since 1.0.0
1679 : * @access private
1680 : */
1681 : function change_case($instr) {
1682 0 : if ($this->language_data['CASE_KEYWORDS'] == GESHI_CAPS_UPPER) {
1683 0 : return strtoupper($instr);
1684 0 : } elseif ($this->language_data['CASE_KEYWORDS'] == GESHI_CAPS_LOWER) {
1685 0 : return strtolower($instr);
1686 : }
1687 0 : return $instr;
1688 : }
1689 : /**
1690 : * Adds a url to a keyword where needed.
1691 : *
1692 : * @param string The keyword to add the URL HTML to
1693 : * @param int What group the keyword is from
1694 : * @param boolean Whether to get the HTML for the start or end
1695 : * @return The HTML for either the start or end of the HTML <a> tag
1696 : * @since 1.0.2
1697 : * @access private
1698 : * @todo Get rid of ender
1699 : */
1700 : function add_url_to_keyword($keyword, $group, $start_or_end) {
1701 0 : if (isset($this->language_data['URLS'][$group]) && $this->language_data['URLS'][$group] != '' && substr($keyword, 0, 5) != '</') {
1702 : // There is a base group for this keyword
1703 0 : if ($start_or_end == 'BEGIN') {
1704 : // HTML workaround... not good form (tm) but should work for 1.0.X
1705 0 : if ($keyword != '') {
1706 : // Old system: strtolower
1707 : //$keyword = ( $this->language_data['CASE_SENSITIVE'][$group] ) ? $keyword : strtolower($keyword);
1708 : // New system: get keyword from language file to get correct case
1709 0 : foreach($this->language_data['KEYWORDS'][$group] as $word) {
1710 0 : if (strtolower($word) == strtolower($keyword)) {
1711 0 : break;
1712 : }
1713 0 : }
1714 0 : $word = (substr($word, 0, 4) == '<') ? substr($word, 4) : $word;
1715 0 : $word = (substr($word, -4) == '>') ? substr($word, 0, strlen($word) -4) : $word;
1716 0 : if (!$word) return '';
1717 0 : return '<|UR1|"'.str_replace(array('{FNAME}', '.'), array(@htmlspecialchars($word, ENT_COMPAT, $this->encoding), '<DOT>'), $this->language_data['URLS'][$group]) .'">';
1718 : }
1719 0 : return '';
1720 : // HTML fix. Again, dirty hackage...
1721 :
1722 0 : } elseif (!($this->language == 'html4strict' && '>' == $keyword)) {
1723 0 : return '</a>';
1724 : }
1725 0 : }
1726 0 : }
1727 : /**
1728 : * Takes a string that has no strings or comments in it, and highlights
1729 : * stuff like keywords, numbers and methods.
1730 : *
1731 : * @param string The string to parse for keyword, numbers etc.
1732 : * @since 1.0.0
1733 : * @access private
1734 : * @todo BUGGY! Why? Why not build string and return?
1735 : */
1736 : function parse_non_string_part(&$stuff_to_parse) {
1737 0 : $stuff_to_parse = ' '.@htmlspecialchars($stuff_to_parse, ENT_COMPAT, $this->encoding);
1738 0 : $stuff_to_parse_pregquote = preg_quote($stuff_to_parse, '/');
1739 0 : $func = '$this->change_case';
1740 0 : $func2 = '$this->add_url_to_keyword';
1741 : //
1742 : // Regular expressions
1743 : //
1744 0 : foreach($this->language_data['REGEXPS'] as $key => $regexp) {
1745 0 : if ($this->lexic_permissions['REGEXPS'][$key]) {
1746 0 : if (is_array($regexp)) {
1747 0 : $stuff_to_parse = preg_replace("/".str_replace('/', '\/', $regexp[GESHI_SEARCH]) ."/{$regexp[GESHI_MODIFIERS]}", "{$regexp[GESHI_BEFORE]}<|!REG3XP$key!>{$regexp[GESHI_REPLACE]}|>{$regexp[GESHI_AFTER]}", $stuff_to_parse);
1748 0 : } else {
1749 0 : $stuff_to_parse = preg_replace("/(".str_replace('/', '\/', $regexp) .")/", "<|!REG3XP$key!>\\1|>", $stuff_to_parse);
1750 : }
1751 0 : }
1752 0 : }
1753 : //
1754 : // Highlight numbers. This regexp sucks... anyone with a regexp that WORKS
1755 : // here wins a cookie if they send it to me. At the moment there's two doing
1756 : // almost exactly the same thing, except the second one prevents a number
1757 : // being highlighted twice (eg <span...><span...>5</span></span>)
1758 : // Put /NUM!/ in for the styles, which gets replaced at the end.
1759 : //
1760 : // NEW ONE: Brice Bernard
1761 : // $stuff_to_parse = preg_replace('/([^(\\w|#|\\\|"|\')])(\\d+)/', '\\1<|/NUM!/>\\2|>', $stuff_to_parse);
1762 : //$stuff_to_parse = preg_replace('/([-+]?\\b(?:[0-9]*\\.)?[0-9]+\\b)/', '<|/NUM!/>\\1|>', $stuff_to_parse);
1763 : //
1764 0 : if ($this->lexic_permissions['NUMBERS'] && preg_match('#[0-9]#', $stuff_to_parse)) {
1765 : //$stuff_to_parse = preg_replace('#([^a-zA-Z0-9_\#])([0-9]+)([^a-zA-Z0-9])#', "\\1<|/NUM!/>\\2|>\\3", $stuff_to_parse);
1766 : //$stuff_to_parse = preg_replace('#([^a-zA-Z0-9_\#>])([0-9]+)([^a-zA-Z0-9])#', "\\1<|/NUM!/>\\2|>\\3", $stuff_to_parse);
1767 0 : $stuff_to_parse = preg_replace('/([-+]?\\b(?:[0-9]*\\.)?[0-9]+\\b)/', '<|/NUM!/>\\1|>', $stuff_to_parse);
1768 0 : }
1769 : // Highlight keywords
1770 : // if there is a couple of alpha symbols there *might* be a keyword
1771 0 : if (preg_match('#[a-zA-Z]{2,}#', $stuff_to_parse)) {
1772 0 : foreach($this->language_data['KEYWORDS'] as $k => $keywordset) {
1773 0 : if ($this->lexic_permissions['KEYWORDS'][$k]) {
1774 0 : foreach($keywordset as $keyword) {
1775 0 : $keyword = preg_quote($keyword, '/');
1776 : //
1777 : // This replacement checks the word is on it's own (except if brackets etc
1778 : // are next to it), then highlights it. We don't put the color=" for the span
1779 : // in just yet - otherwise languages with the keywords "color" or "or" have
1780 : // a fit.
1781 : //
1782 0 : if (false !== stristr($stuff_to_parse_pregquote, $keyword)) {
1783 0 : $stuff_to_parse.= ' ';
1784 : // Might make a more unique string for putting the number in soon
1785 : // Basically, we don't put the styles in yet because then the styles themselves will
1786 : // get highlighted if the language has a CSS keyword in it (like CSS, for example ;))
1787 0 : $styles = "/$k/";
1788 0 : if ($this->language_data['CASE_SENSITIVE'][$k]) {
1789 0 : $stuff_to_parse = preg_replace("/([^a-zA-Z0-9\$_\|\#;>|^])($keyword)(?=[^a-zA-Z0-9_<\|%\-&])/e", "'\\1' . $func2('\\2', '$k', 'BEGIN') . '<|$styles>' . $func('\\2') . '|>' . $func2('\\2', '$k', 'END')", $stuff_to_parse);
1790 0 : } else {
1791 : // Change the case of the word.
1792 : // hackage again... must... release... 1.2...
1793 0 : if ('smarty' == $this->language) {
1794 0 : $hackage = '\/';
1795 0 : } else {
1796 0 : $hackage = '';
1797 : }
1798 0 : $stuff_to_parse = preg_replace("/([^a-zA-Z0-9\$_\|\#;>$hackage|^])($keyword)(?=[^a-zA-Z0-9_<\|%\-&])/ie", "'\\1' . $func2('\\2', '$k', 'BEGIN') . '<|$styles>' . $func('\\2') . '|>' . $func2('\\2', '$k', 'END')", $stuff_to_parse);
1799 : }
1800 0 : $stuff_to_parse = substr($stuff_to_parse, 0, strlen($stuff_to_parse) -1);
1801 0 : }
1802 0 : }
1803 0 : }
1804 0 : }
1805 0 : }
1806 : //
1807 : // Now that's all done, replace /[number]/ with the correct styles
1808 : //
1809 0 : foreach($this->language_data['KEYWORDS'] as $k => $kws) {
1810 0 : if (!$this->use_classes) {
1811 0 : $attributes = ' style="'.$this->language_data['STYLES']['KEYWORDS'][$k].'"';
1812 0 : } else {
1813 0 : $attributes = ' class="kw'.$k.'"';
1814 : }
1815 0 : $stuff_to_parse = str_replace("/$k/", $attributes, $stuff_to_parse);
1816 0 : }
1817 : // Put number styles in
1818 0 : if (!$this->use_classes && $this->lexic_permissions['NUMBERS']) {
1819 0 : $attributes = ' style="'.$this->language_data['STYLES']['NUMBERS'][0].'"';
1820 0 : } else {
1821 0 : $attributes = ' class="nu0"';
1822 : }
1823 0 : $stuff_to_parse = str_replace('/NUM!/', $attributes, $stuff_to_parse);
1824 : //
1825 : // Highlight methods and fields in objects
1826 : //
1827 0 : if ($this->lexic_permissions['METHODS'] && $this->language_data['OOLANG']) {
1828 0 : foreach($this->language_data['OBJECT_SPLITTERS'] as $key => $splitter) {
1829 0 : if (false !== stristr($stuff_to_parse, $splitter)) {
1830 0 : if (!$this->use_classes) {
1831 0 : $attributes = ' style="'.$this->language_data['STYLES']['METHODS'][$key].'"';
1832 0 : } else {
1833 0 : $attributes = ' class="me'.$key.'"';
1834 : }
1835 0 : $stuff_to_parse = preg_replace("/(".preg_quote($this->language_data['OBJECT_SPLITTERS'][$key], 1) ."[\s]*)([a-zA-Z\*\(][a-zA-Z0-9_\*]*)/", "\\1<|$attributes>\\2|>", $stuff_to_parse);
1836 0 : }
1837 0 : }
1838 0 : }
1839 : //
1840 : // Highlight brackets. Yes, I've tried adding a semi-colon to this list.
1841 : // You try it, and see what happens ;)
1842 : // TODO: Fix lexic permissions not converting entities if shouldn't
1843 : // be highlighting regardless
1844 : //
1845 0 : if ($this->lexic_permissions['BRACKETS']) {
1846 0 : $code_entities_match = array('[', ']', '(', ')', '{', '}');
1847 0 : if (!$this->use_classes) {
1848 0 : $code_entities_replace = array('<| style="'.$this->language_data['STYLES']['BRACKETS'][0].'">[|>', '<| style="'.$this->language_data['STYLES']['BRACKETS'][0].'">]|>', '<| style="'.$this->language_data['STYLES']['BRACKETS'][0].'">(|>', '<| style="'.$this->language_data['STYLES']['BRACKETS'][0].'">)|>', '<| style="'.$this->language_data['STYLES']['BRACKETS'][0].'">{|>', '<| style="'.$this->language_data['STYLES']['BRACKETS'][0].'">}|>',);
1849 0 : } else {
1850 0 : $code_entities_replace = array('<| class="br0">[|>', '<| class="br0">]|>', '<| class="br0">(|>', '<| class="br0">)|>', '<| class="br0">{|>', '<| class="br0">}|>',);
1851 : }
1852 0 : $stuff_to_parse = str_replace($code_entities_match, $code_entities_replace, $stuff_to_parse);
1853 0 : }
1854 : //
1855 : // Add class/style for regexps
1856 : //
1857 0 : foreach($this->language_data['REGEXPS'] as $key => $regexp) {
1858 0 : if ($this->lexic_permissions['REGEXPS'][$key]) {
1859 0 : if (!$this->use_classes) {
1860 0 : $attributes = ' style="'.$this->language_data['STYLES']['REGEXPS'][$key].'"';
1861 0 : } else {
1862 0 : if (is_array($this->language_data['REGEXPS'][$key]) && array_key_exists(GESHI_CLASS, $this->language_data['REGEXPS'][$key])) {
1863 0 : $attributes = ' class="'.$this->language_data['REGEXPS'][$key][GESHI_CLASS].'"';
1864 0 : } else {
1865 0 : $attributes = ' class="re'.$key.'"';
1866 : }
1867 : }
1868 0 : $stuff_to_parse = str_replace("!REG3XP$key!", "$attributes", $stuff_to_parse);
1869 0 : }
1870 0 : }
1871 : // Replace <DOT> with . for urls
1872 0 : $stuff_to_parse = str_replace('<DOT>', '.', $stuff_to_parse);
1873 : // Replace <|UR1| with <a href= for urls also
1874 0 : if (isset($this->link_styles[GESHI_LINK])) {
1875 0 : if ($this->use_classes) {
1876 0 : $stuff_to_parse = str_replace('<|UR1|', '<a'.$this->link_target.' href=', $stuff_to_parse);
1877 0 : } else {
1878 0 : $stuff_to_parse = str_replace('<|UR1|', '<a'.$this->link_target.' style="'.$this->link_styles[GESHI_LINK].'" href=', $stuff_to_parse);
1879 : }
1880 0 : } else {
1881 0 : $stuff_to_parse = str_replace('<|UR1|', '<a'.$this->link_target.' href=', $stuff_to_parse);
1882 : }
1883 : //
1884 : // NOW we add the span thingy ;)
1885 : //
1886 0 : $stuff_to_parse = str_replace('<|', '<span', $stuff_to_parse);
1887 0 : $stuff_to_parse = str_replace('|>', '</span>', $stuff_to_parse);
1888 0 : return substr($stuff_to_parse, 1);
1889 : }
1890 : /**
1891 : * Sets the time taken to parse the code
1892 : *
1893 : * @param microtime The time when parsing started
1894 : * @param microtime The time when parsing ended
1895 : * @since 1.0.2
1896 : * @access private
1897 : */
1898 : function set_time($start_time, $end_time) {
1899 0 : $start = explode(' ', $start_time);
1900 0 : $end = explode(' ', $end_time);
1901 0 : $this->time = $end[0]+$end[1]-$start[0]-$start[1];
1902 0 : }
1903 : /**
1904 : * Gets the time taken to parse the code
1905 : *
1906 : * @return double The time taken to parse the code
1907 : * @since 1.0.2
1908 : */
1909 : function get_time() {
1910 0 : return $this->time;
1911 : }
1912 : /**
1913 : * Gets language information and stores it for later use
1914 : *
1915 : * @access private
1916 : * @todo Needs to load keys for lexic permissions for keywords, regexps etc
1917 : */
1918 : function load_language($file_name) {
1919 0 : $this->enable_highlighting();
1920 0 : $language_data = array();
1921 0 : require $file_name;
1922 : // Perhaps some checking might be added here later to check that
1923 : // $language data is a valid thing but maybe not
1924 0 : $this->language_data = $language_data;
1925 : // Set strict mode if should be set
1926 0 : if ($this->language_data['STRICT_MODE_APPLIES'] == GESHI_ALWAYS) {
1927 0 : $this->strict_mode = true;
1928 0 : }
1929 : // Set permissions for all lexics to true
1930 : // so they'll be highlighted by default
1931 0 : foreach($this->language_data['KEYWORDS'] as $key => $words) {
1932 0 : $this->lexic_permissions['KEYWORDS'][$key] = true;
1933 0 : }
1934 0 : foreach($this->language_data['COMMENT_SINGLE'] as $key => $comment) {
1935 0 : $this->lexic_permissions['COMMENTS'][$key] = true;
1936 0 : }
1937 0 : foreach($this->language_data['REGEXPS'] as $key => $regexp) {
1938 0 : $this->lexic_permissions['REGEXPS'][$key] = true;
1939 0 : }
1940 : // Set default class for CSS
1941 0 : $this->overall_class = $this->language;
1942 0 : }
1943 : /**
1944 : * Takes the parsed code and various options, and creates the HTML
1945 : * surrounding it to make it look nice.
1946 : *
1947 : * @param string The code already parsed
1948 : * @return string The code nicely finalised
1949 : * @since 1.0.0
1950 : * @access private
1951 : */
1952 : function finalise($parsed_code) {
1953 : // Remove end parts of important declarations
1954 : // This is BUGGY!! My fault for bad code: fix coming in 1.2
1955 : // @todo Remove this crap
1956 0 : if ($this->enable_important_blocks && (strstr($parsed_code, @htmlspecialchars(GESHI_START_IMPORTANT, ENT_COMPAT, $this->encoding)) === false)) {
1957 0 : $parsed_code = str_replace(@htmlspecialchars(GESHI_END_IMPORTANT, ENT_COMPAT, $this->encoding), '', $parsed_code);
1958 0 : }
1959 : // Add HTML whitespace stuff if we're using the <div> header
1960 0 : if ($this->header_type != GESHI_HEADER_PRE) {
1961 0 : $parsed_code = $this->indent($parsed_code);
1962 0 : }
1963 : // purge some unnecessary stuff
1964 0 : $parsed_code = preg_replace('#<span[^>]+>(\s*)</span>#', '\\1', $parsed_code);
1965 0 : $parsed_code = preg_replace('#<div[^>]+>(\s*)</div>#', '\\1', $parsed_code);
1966 : // If we are using IDs for line numbers, there needs to be an overall
1967 : // ID set to prevent collisions.
1968 0 : if ($this->add_ids && !$this->overall_id) {
1969 0 : $this->overall_id = 'geshi-'.substr(md5(microtime()), 0, 4);
1970 0 : }
1971 : // If we're using line numbers, we insert <li>s and appropriate
1972 : // markup to style them (otherwise we don't need to do anything)
1973 0 : if ($this->line_numbers != GESHI_NO_LINE_NUMBERS) {
1974 : // If we're using the <pre> header, we shouldn't add newlines because
1975 : // the <pre> will line-break them (and the <li>s already do this for us)
1976 0 : $ls = ($this->header_type != GESHI_HEADER_PRE) ? "\n" : '';
1977 : // Get code into lines
1978 0 : $code = explode("\n", $parsed_code);
1979 : // Set vars to defaults for following loop
1980 0 : $parsed_code = '';
1981 0 : $i = 0;
1982 0 : $attrs = array();
1983 : // Foreach line...
1984 0 : foreach($code as $line) { //echo "LINE:|".htmlspecialchars($line)."| ".strlen($line)."<br />\n";
1985 0 : if ('' == $line || ' ' == $line) {
1986 0 : $line = ' ';
1987 0 : }
1988 : // If this is a "special line"...
1989 0 : if ($this->line_numbers == GESHI_FANCY_LINE_NUMBERS && $i%$this->line_nth_row == ($this->line_nth_row-1)) {
1990 : // Set the attributes to style the line
1991 0 : if ($this->use_classes) {
1992 : //$attr = ' class="li2"';
1993 0 : $attrs['class'][] = 'li2';
1994 0 : $def_attr = ' class="de2"';
1995 0 : } else {
1996 : //$attr = ' style="' . $this->line_style2 . '"';
1997 0 : $attrs['style'][] = $this->line_style2;
1998 : // This style "covers up" the special styles set for special lines
1999 : // so that styles applied to special lines don't apply to the actual
2000 : // code on that line
2001 0 : $def_attr = ' style="'.$this->code_style.'"';
2002 : }
2003 : // Span or div?
2004 0 : $start = "<div$def_attr>";
2005 0 : $end = '</div>';
2006 0 : } else {
2007 0 : if ($this->use_classes) {
2008 : //$attr = ' class="li1"';
2009 0 : $attrs['class'][] = 'li1';
2010 0 : $def_attr = ' class="de1"';
2011 0 : } else {
2012 : //$attr = ' style="' . $this->line_style1 . '"';
2013 0 : $attrs['style'][] = $this->line_style1;
2014 0 : $def_attr = ' style="'.$this->code_style.'"';
2015 : }
2016 0 : $start = "<div$def_attr>";
2017 0 : $end = '</div>';
2018 : }
2019 0 : ++$i;
2020 : // Are we supposed to use ids? If so, add them
2021 0 : if ($this->add_ids) {
2022 0 : $attrs['id'][] = "$this->overall_id-$i";
2023 0 : }
2024 0 : if ($this->use_classes && in_array($i, $this->highlight_extra_lines)) {
2025 0 : $attrs['class'][] = 'ln-xtra';
2026 0 : }
2027 0 : if (!$this->use_classes && in_array($i, $this->highlight_extra_lines)) {
2028 0 : $attrs['style'][] = $this->highlight_extra_lines_style;
2029 0 : }
2030 : // Add in the line surrounded by appropriate list HTML
2031 0 : $attr_string = ' ';
2032 0 : foreach($attrs as $key => $attr) {
2033 0 : $attr_string.= $key.'="'.implode(' ', $attr) .'" ';
2034 0 : }
2035 0 : $attr_string = substr($attr_string, 0, -1);
2036 0 : $parsed_code.= "<li$attr_string>$start$line$end</li>$ls";
2037 0 : $attrs = array();
2038 0 : }
2039 0 : } else {
2040 : // No line numbers, but still need to handle highlighting lines extra.
2041 : // Have to use divs so the full width of the code is highlighted
2042 0 : $code = explode("\n", $parsed_code);
2043 0 : $parsed_code = '';
2044 0 : $i = 0;
2045 0 : foreach($code as $line) {
2046 : // Make lines have at least one space in them if they're empty
2047 0 : if ('' == $line || ' ' == $line) {
2048 0 : $line = ' ';
2049 0 : }
2050 0 : if (in_array(++$i, $this->highlight_extra_lines)) {
2051 0 : if ($this->use_classes) {
2052 0 : $parsed_code.= '<div class="ln-xtra">';
2053 0 : } else {
2054 0 : $parsed_code.= "<div style=\"{$this->highlight_extra_lines_style}\">";
2055 : }
2056 : // Remove \n because it stuffs up <pre> header
2057 0 : $parsed_code.= $line."</div>";
2058 0 : } else {
2059 0 : $parsed_code.= $line."\n";
2060 : }
2061 0 : }
2062 : }
2063 0 : if ($this->header_type == GESHI_HEADER_PRE) {
2064 : // enforce line numbers when using pre
2065 0 : $parsed_code = str_replace('<li></li>', '<li> </li>', $parsed_code);
2066 0 : }
2067 0 : return $this->header() .chop($parsed_code) .$this->footer();
2068 : }
2069 : /**
2070 : * Creates the header for the code block (with correct attributes)
2071 : *
2072 : * @return string The header for the code block
2073 : * @since 1.0.0
2074 : * @access private
2075 : */
2076 : function header() {
2077 : // Get attributes needed
2078 0 : $attributes = $this->get_attributes();
2079 0 : $ol_attributes = '';
2080 0 : if ($this->line_numbers_start != 1) {
2081 0 : $ol_attributes.= ' start="'.$this->line_numbers_start.'"';
2082 0 : }
2083 : // Get the header HTML
2084 0 : $header = $this->format_header_content();
2085 0 : if (GESHI_HEADER_NONE == $this->header_type) {
2086 0 : if ($this->line_numbers != GESHI_NO_LINE_NUMBERS) {
2087 0 : return "$header<ol$ol_attributes>";
2088 : }
2089 0 : return $header;
2090 : }
2091 : // Work out what to return and do it
2092 0 : if ($this->line_numbers != GESHI_NO_LINE_NUMBERS) {
2093 0 : if ($this->header_type == GESHI_HEADER_PRE) {
2094 0 : return "<pre$attributes>$header<ol$ol_attributes>";
2095 0 : } elseif ($this->header_type == GESHI_HEADER_DIV) {
2096 0 : return "<div$attributes>$header<ol$ol_attributes>";
2097 : }
2098 0 : } else {
2099 0 : if ($this->header_type == GESHI_HEADER_PRE) {
2100 0 : return "<pre$attributes>$header";
2101 0 : } elseif ($this->header_type == GESHI_HEADER_DIV) {
2102 0 : return "<div$attributes>$header";
2103 : }
2104 : }
2105 0 : }
2106 : /**
2107 : * Returns the header content, formatted for output
2108 : *
2109 : * @return string The header content, formatted for output
2110 : * @since 1.0.2
2111 : * @access private
2112 : */
2113 : function format_header_content() {
2114 0 : $header = $this->header_content;
2115 0 : if ($header) {
2116 0 : if ($this->header_type == GESHI_HEADER_PRE) {
2117 0 : $header = str_replace("\n", '', $header);
2118 0 : }
2119 0 : $header = $this->replace_keywords($header);
2120 0 : if ($this->use_classes) {
2121 0 : $attr = ' class="head"';
2122 0 : } else {
2123 0 : $attr = " style=\"{$this->header_content_style}\"";
2124 : }
2125 0 : return "<div$attr>$header</div>";
2126 : }
2127 0 : }
2128 : /**
2129 : * Returns the footer for the code block.
2130 : *
2131 : * @return string The footer for the code block
2132 : * @since 1.0.0
2133 : * @access private
2134 : */
2135 : function footer() {
2136 0 : $footer_content = $this->format_footer_content();
2137 0 : if (GESHI_HEADER_NONE == $this->header_type) {
2138 0 : return ($this->line_numbers != GESHI_NO_LINE_NUMBERS) ? '</ol>'.$footer_content : $footer_content;
2139 : }
2140 0 : if ($this->header_type == GESHI_HEADER_DIV) {
2141 0 : if ($this->line_numbers != GESHI_NO_LINE_NUMBERS) {
2142 0 : return "</ol>$footer_content</div>";
2143 : }
2144 0 : return "$footer_content</div>";
2145 : } else {
2146 0 : if ($this->line_numbers != GESHI_NO_LINE_NUMBERS) {
2147 0 : return "</ol>$footer_content</pre>";
2148 : }
2149 0 : return "$footer_content</pre>";
2150 : }
2151 : }
2152 : /**
2153 : * Returns the footer content, formatted for output
2154 : *
2155 : * @return string The footer content, formatted for output
2156 : * @since 1.0.2
2157 : * @access private
2158 : */
2159 : function format_footer_content() {
2160 0 : $footer = $this->footer_content;
2161 0 : if ($footer) {
2162 0 : if ($this->header_type == GESHI_HEADER_PRE) {
2163 0 : $footer = str_replace("\n", '', $footer);;
2164 0 : }
2165 0 : $footer = $this->replace_keywords($footer);
2166 0 : if ($this->use_classes) {
2167 0 : $attr = ' class="foot"';
2168 0 : } else {
2169 0 : $attr = " style=\"{$this->footer_content_style}\"";
2170 : }
2171 0 : return "<div$attr>$footer</div>";
2172 : }
2173 0 : }
2174 : /**
2175 : * Replaces certain keywords in the header and footer with
2176 : * certain configuration values
2177 : *
2178 : * @param string The header or footer content to do replacement on
2179 : * @return string The header or footer with replaced keywords
2180 : * @since 1.0.2
2181 : * @access private
2182 : */
2183 : function replace_keywords($instr) {
2184 0 : $keywords = $replacements = array();
2185 0 : $keywords[] = '<TIME>';
2186 0 : $replacements[] = number_format($this->get_time(), 3);
2187 0 : $keywords[] = '<LANGUAGE>';
2188 0 : $replacements[] = $this->language;
2189 0 : $keywords[] = '<VERSION>';
2190 0 : $replacements[] = GESHI_VERSION;
2191 0 : return str_replace($keywords, $replacements, $instr);
2192 : }
2193 : /**
2194 : * Gets the CSS attributes for this code
2195 : *
2196 : * @return The CSS attributes for this code
2197 : * @since 1.0.0
2198 : * @access private
2199 : * @todo Document behaviour change - class is outputted regardless of whether we're using classes or not.
2200 : * Same with style
2201 : */
2202 : function get_attributes() {
2203 0 : $attributes = '';
2204 0 : if ($this->overall_class != '') {
2205 0 : $attributes.= " class=\"{$this->overall_class}\"";
2206 0 : }
2207 0 : if ($this->overall_id != '') {
2208 0 : $attributes.= " id=\"{$this->overall_id}\"";
2209 0 : }
2210 0 : if ($this->overall_style != '') {
2211 0 : $attributes.= ' style="'.$this->overall_style.'"';
2212 0 : }
2213 0 : return $attributes;
2214 : }
2215 : /**
2216 : * Returns a stylesheet for the highlighted code. If $economy mode
2217 : * is true, we only return the stylesheet declarations that matter for
2218 : * this code block instead of the whole thing
2219 : *
2220 : * @param boolean Whether to use economy mode or not
2221 : * @return string A stylesheet built on the data for the current language
2222 : * @since 1.0.0
2223 : */
2224 : function get_stylesheet($economy_mode = true) {
2225 : // If there's an error, chances are that the language file
2226 : // won't have populated the language data file, so we can't
2227 : // risk getting a stylesheet...
2228 0 : if ($this->error) {
2229 0 : return '';
2230 : }
2231 : // First, work out what the selector should be. If there's an ID,
2232 : // that should be used, the same for a class. Otherwise, a selector
2233 : // of '' means that these styles will be applied anywhere
2234 0 : $selector = ($this->overall_id != '') ? "#{$this->overall_id} " : '';
2235 0 : $selector = ($selector == '' && $this->overall_class != '') ? ".{$this->overall_class} " : $selector;
2236 : // Header of the stylesheet
2237 0 : if (!$economy_mode) {
2238 0 : $stylesheet = "/**\n * GeSHi Dynamically Generated Stylesheet\n * --------------------------------------\n * Dynamically generated stylesheet for {$this->language}\n * CSS class: {$this->overall_class}, CSS id: {$this->overall_id}\n * GeSHi (c) Nigel McNie 2004 (http://qbnz.com/highlighter)\n */\n";
2239 0 : } else {
2240 0 : $stylesheet = '/* GeSHi (c) Nigel McNie 2004 (http://qbnz.com/highlighter) */'."\n";
2241 : }
2242 : // Set the <ol> to have no effect at all if there are line numbers
2243 : // (<ol>s have margins that should be destroyed so all layout is
2244 : // controlled by the set_overall_style method, which works on the
2245 : // <pre> or <div> container). Additionally, set default styles for lines
2246 0 : if (!$economy_mode || $this->line_numbers != GESHI_NO_LINE_NUMBERS) {
2247 : //$stylesheet .= "$selector, {$selector}ol, {$selector}ol li {margin: 0;}\n";
2248 0 : $stylesheet.= "$selector.de1, $selector.de2 {{$this->code_style}}\n";
2249 0 : }
2250 : // Add overall styles
2251 0 : if (!$economy_mode || $this->overall_style != '') {
2252 0 : $stylesheet.= "$selector {{$this->overall_style}}\n";
2253 0 : }
2254 : // Add styles for links
2255 0 : foreach($this->link_styles as $key => $style) {
2256 0 : if (!$economy_mode || $key == GESHI_LINK && $style != '') {
2257 0 : $stylesheet.= "{$selector}a:link {{$style}}\n";
2258 0 : }
2259 0 : if (!$economy_mode || $key == GESHI_HOVER && $style != '') {
2260 0 : $stylesheet.= "{$selector}a:hover {{$style}}\n";
2261 0 : }
2262 0 : if (!$economy_mode || $key == GESHI_ACTIVE && $style != '') {
2263 0 : $stylesheet.= "{$selector}a:active {{$style}}\n";
2264 0 : }
2265 0 : if (!$economy_mode || $key == GESHI_VISITED && $style != '') {
2266 0 : $stylesheet.= "{$selector}a:visited {{$style}}\n";
2267 0 : }
2268 0 : }
2269 : // Header and footer
2270 0 : if (!$economy_mode || $this->header_content_style != '') {
2271 0 : $stylesheet.= "$selector.head {{$this->header_content_style}}\n";
2272 0 : }
2273 0 : if (!$economy_mode || $this->footer_content_style != '') {
2274 0 : $stylesheet.= "$selector.foot {{$this->footer_content_style}}\n";
2275 0 : }
2276 : // Styles for important stuff
2277 0 : if (!$economy_mode || $this->important_styles != '') {
2278 0 : $stylesheet.= "$selector.imp {{$this->important_styles}}\n";
2279 0 : }
2280 : // Styles for lines being highlighted extra
2281 0 : if (!$economy_mode || count($this->highlight_extra_lines)) {
2282 0 : $stylesheet.= "$selector.ln-xtra {{$this->highlight_extra_lines_style}}\n";
2283 0 : }
2284 : // Simple line number styles
2285 0 : if (!$economy_mode || ($this->line_numbers != GESHI_NO_LINE_NUMBERS && $this->line_style1 != '')) {
2286 0 : $stylesheet.= "{$selector}li {{$this->line_style1}}\n";
2287 0 : }
2288 : // If there is a style set for fancy line numbers, echo it out
2289 0 : if (!$economy_mode || ($this->line_numbers == GESHI_FANCY_LINE_NUMBERS && $this->line_style2 != '')) {
2290 0 : $stylesheet.= "{$selector}li.li2 {{$this->line_style2}}\n";
2291 0 : }
2292 0 : foreach($this->language_data['STYLES']['KEYWORDS'] as $group => $styles) {
2293 0 : if (!$economy_mode || !($economy_mode && (!$this->lexic_permissions['KEYWORDS'][$group] || $styles == ''))) {
2294 0 : $stylesheet.= "$selector.kw$group {{$styles}}\n";
2295 0 : }
2296 0 : }
2297 0 : foreach($this->language_data['STYLES']['COMMENTS'] as $group => $styles) {
2298 0 : if (!$economy_mode || !($economy_mode && $styles == '') && !($economy_mode && !$this->lexic_permissions['COMMENTS'][$group])) {
2299 0 : $stylesheet.= "$selector.co$group {{$styles}}\n";
2300 0 : }
2301 0 : }
2302 0 : foreach($this->language_data['STYLES']['ESCAPE_CHAR'] as $group => $styles) {
2303 0 : if (!$economy_mode || !($economy_mode && $styles == '') && !($economy_mode && !$this->lexic_permissions['ESCAPE_CHAR'])) {
2304 0 : $stylesheet.= "$selector.es$group {{$styles}}\n";
2305 0 : }
2306 0 : }
2307 0 : foreach($this->language_data['STYLES']['SYMBOLS'] as $group => $styles) {
2308 0 : if (!$economy_mode || !($economy_mode && $styles == '') && !($economy_mode && !$this->lexic_permissions['BRACKETS'])) {
2309 0 : $stylesheet.= "$selector.br$group {{$styles}}\n";
2310 0 : }
2311 0 : }
2312 0 : foreach($this->language_data['STYLES']['STRINGS'] as $group => $styles) {
2313 0 : if (!$economy_mode || !($economy_mode && $styles == '') && !($economy_mode && !$this->lexic_permissions['STRINGS'])) {
2314 0 : $stylesheet.= "$selector.st$group {{$styles}}\n";
2315 0 : }
2316 0 : }
2317 0 : foreach($this->language_data['STYLES']['NUMBERS'] as $group => $styles) {
2318 0 : if (!$economy_mode || !($economy_mode && $styles == '') && !($economy_mode && !$this->lexic_permissions['NUMBERS'])) {
2319 0 : $stylesheet.= "$selector.nu$group {{$styles}}\n";
2320 0 : }
2321 0 : }
2322 0 : foreach($this->language_data['STYLES']['METHODS'] as $group => $styles) {
2323 0 : if (!$economy_mode || !($economy_mode && $styles == '') && !($economy_mode && !$this->lexic_permissions['METHODS'])) {
2324 0 : $stylesheet.= "$selector.me$group {{$styles}}\n";
2325 0 : }
2326 0 : }
2327 0 : foreach($this->language_data['STYLES']['SCRIPT'] as $group => $styles) {
2328 0 : if (!$economy_mode || !($economy_mode && $styles == '')) {
2329 0 : $stylesheet.= "$selector.sc$group {{$styles}}\n";
2330 0 : }
2331 0 : }
2332 0 : foreach($this->language_data['STYLES']['REGEXPS'] as $group => $styles) {
2333 0 : if (!$economy_mode || !($economy_mode && $styles == '') && !($economy_mode && !$this->lexic_permissions['REGEXPS'][$group])) {
2334 0 : if (is_array($this->language_data['REGEXPS'][$group]) && array_key_exists(GESHI_CLASS, $this->language_data['REGEXPS'][$group])) {
2335 0 : $stylesheet.= "$selector.";
2336 0 : $stylesheet.= $this->language_data['REGEXPS'][$group][GESHI_CLASS];
2337 0 : $stylesheet.= " {{$styles}}\n";
2338 0 : } else {
2339 0 : $stylesheet.= "$selector.re$group {{$styles}}\n";
2340 : }
2341 0 : }
2342 0 : }
2343 0 : return $stylesheet;
2344 : }
2345 : } // End Class GeSHi
2346 : if (!function_exists('geshi_highlight')) {
2347 : /**
2348 : * Easy way to highlight stuff. Behaves just like highlight_string
2349 : *
2350 : * @param string The code to highlight
2351 : * @param string The language to highlight the code in
2352 : * @param string The path to the language files. You can leave this blank if you need
2353 : * as from version 1.0.7 the path should be automatically detected
2354 : * @param boolean Whether to return the result or to echo
2355 : * @return string The code highlighted (if $return is true)
2356 : * @since 1.0.2
2357 : */
2358 : function geshi_highlight($string, $language, $path, $return = false) {
2359 0 : $geshi = new GeSHi($string, $language, $path);
2360 0 : $geshi->set_header_type(GESHI_HEADER_NONE);
2361 0 : if ($return) {
2362 0 : return '<code>'.$geshi->parse_code() .'</code>';
2363 : }
2364 0 : echo '<code>'.$geshi->parse_code() .'</code>';
2365 0 : if ($geshi->error()) {
2366 0 : return false;
2367 : }
2368 0 : return true;
2369 : }
2370 : }
|